blob: 6c31ad362f54512790edbfb5fa0d3412869c76eb [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 Ponsca00dec2020-05-02 15:04:00 +0200575 IOSAV_SUBSEQUENCE(channel, 0,
576 IOSAV_ZQCS & NO_RANKSEL,
577 1, 3, 8, SSQ_NA,
578 0, 6, 0, slotrank,
579 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100580
Angel Pons7c49cb82020-03-16 23:17:32 +0100581 /*
582 * Execute command queue - why is bit 22 set here?!
583 *
584 * This is actually using the IOSAV state machine as a timer, so refresh is allowed.
585 */
Angel Pons88521882020-01-05 20:21:20 +0100586 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = (1 << 22) | IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +0200587
Angel Pons88521882020-01-05 20:21:20 +0100588 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100589}
590
Angel Pons88521882020-01-05 20:21:20 +0100591void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100592{
Felix Held9fe248f2018-07-31 20:59:45 +0200593 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100594 int channel;
595
Angel Pons7c49cb82020-03-16 23:17:32 +0100596 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
597 ;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100598 do {
Angel Pons88521882020-01-05 20:21:20 +0100599 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100600 } while ((reg & 0x14) == 0);
601
Angel Pons7c49cb82020-03-16 23:17:32 +0100602 /* Set state of memory controller */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100603 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100604 MCHBAR32(MC_INIT_STATE_G) = reg;
605 MCHBAR32(MC_INIT_STATE) = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100606 reg |= 2; /* DDR reset */
Angel Pons88521882020-01-05 20:21:20 +0100607 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100608
Angel Pons7c49cb82020-03-16 23:17:32 +0100609 /* Assert DIMM reset signal */
610 MCHBAR32_AND(MC_INIT_STATE_G, ~2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100611
Angel Pons7c49cb82020-03-16 23:17:32 +0100612 /* Wait 200us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100613 udelay(200);
614
Angel Pons7c49cb82020-03-16 23:17:32 +0100615 /* Deassert DIMM reset signal */
Angel Pons88521882020-01-05 20:21:20 +0100616 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100617
Angel Pons7c49cb82020-03-16 23:17:32 +0100618 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100619 udelay(500);
620
Angel Pons7c49cb82020-03-16 23:17:32 +0100621 /* Enable DCLK */
Angel Pons88521882020-01-05 20:21:20 +0100622 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100623
Angel Pons7c49cb82020-03-16 23:17:32 +0100624 /* XXX Wait 20ns */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100625 udelay(1);
626
627 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100628 /* Set valid rank CKE */
Felix Held9fe248f2018-07-31 20:59:45 +0200629 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100630 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100631
Angel Pons7c49cb82020-03-16 23:17:32 +0100632 /* Wait 10ns for ranks to settle */
633 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100634
635 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100636 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100637
Angel Pons7c49cb82020-03-16 23:17:32 +0100638 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100639 write_reset(ctrl);
640 }
641}
642
643static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
644{
Angel Pons7c49cb82020-03-16 23:17:32 +0100645 /* Get ODT based on rankmap */
646 int dimms_per_ch = (ctrl->rankmap[channel] & 1) + ((ctrl->rankmap[channel] >> 2) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100647
648 if (dimms_per_ch == 1) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100649 return (const odtmap){60, 60};
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100650 } else {
651 return (const odtmap){120, 30};
652 }
653}
654
Angel Pons7c49cb82020-03-16 23:17:32 +0100655static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank, int reg, u32 val)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100656{
Angel Pons88521882020-01-05 20:21:20 +0100657 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100658
659 if (ctrl->rank_mirror[channel][slotrank]) {
660 /* DDR3 Rank1 Address mirror
Angel Pons7c49cb82020-03-16 23:17:32 +0100661 swap the following pins:
662 A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100663 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
Angel Pons7c49cb82020-03-16 23:17:32 +0100664 val = (val & ~0x1f8) | ((val >> 1) & 0xa8) | ((val & 0xa8) << 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100665 }
666
667 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200668 IOSAV_SUBSEQUENCE(channel, 0,
669 IOSAV_MRS & NO_RANKSEL,
670 1, 4, 4, SSQ_NA,
671 val, 6, reg, slotrank,
672 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100673
674 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200675 IOSAV_SUBSEQUENCE(channel, 1,
676 IOSAV_MRS,
677 1, 4, 4, SSQ_NA,
678 val, 6, reg, slotrank,
679 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100680
681 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200682 IOSAV_SUBSEQUENCE(channel, 2,
683 IOSAV_MRS & NO_RANKSEL,
684 1, 4, ctrl->tMOD, SSQ_NA,
685 val, 6, reg, slotrank,
686 ADDR_UPDATE_NONE);
Felix Held9cf1dd22018-07-31 14:52:40 +0200687
Angel Pons7c49cb82020-03-16 23:17:32 +0100688 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100689 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100690}
691
Angel Pons88521882020-01-05 20:21:20 +0100692static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100693{
694 u16 mr0reg, mch_cas, mch_wr;
695 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 +0100696 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100697
698 /* DLL Reset - self clearing - set after CLK frequency has been changed */
699 mr0reg = 0x100;
700
Angel Pons7c49cb82020-03-16 23:17:32 +0100701 /* Convert CAS to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100702 if (ctrl->CAS < 12) {
703 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
704 } else {
705 mch_cas = (u16) (ctrl->CAS - 12);
706 mch_cas = ((mch_cas << 1) | 0x1);
707 }
708
Angel Pons7c49cb82020-03-16 23:17:32 +0100709 /* Convert tWR to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100710 mch_wr = mch_wr_t[ctrl->tWR - 5];
711
Angel Pons7c49cb82020-03-16 23:17:32 +0100712 mr0reg = (mr0reg & ~0x0004) | ((mch_cas & 0x1) << 2);
713 mr0reg = (mr0reg & ~0x0070) | ((mch_cas & 0xe) << 3);
714 mr0reg = (mr0reg & ~0x0e00) | (mch_wr << 9);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100715
Angel Pons7c49cb82020-03-16 23:17:32 +0100716 /* Precharge PD - Fast (desktop) 1 or slow (mobile) 0 - mostly power-saving feature */
717 mr0reg = (mr0reg & ~(1 << 12)) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100718 return mr0reg;
719}
720
721static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
722{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200723 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100724}
725
726static u32 encode_odt(u32 odt)
727{
728 switch (odt) {
729 case 30:
730 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
731 case 60:
732 return (1 << 2); // RZQ/4
733 case 120:
734 return (1 << 6); // RZQ/2
735 default:
736 case 0:
737 return 0;
738 }
739}
740
741static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
742{
743 odtmap odt;
744 u32 mr1reg;
745
746 odt = get_ODT(ctrl, rank, channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100747 mr1reg = 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100748
749 mr1reg |= encode_odt(odt.rttnom);
750
751 return mr1reg;
752}
753
754static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
755{
756 u16 mr1reg;
757
758 mr1reg = make_mr1(ctrl, rank, channel);
759
760 write_mrreg(ctrl, channel, rank, 1, mr1reg);
761}
762
763static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
764{
765 u16 pasr, cwl, mr2reg;
766 odtmap odt;
767 int srt;
768
769 pasr = 0;
770 cwl = ctrl->CWL - 5;
771 odt = get_ODT(ctrl, rank, channel);
772
773 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
774
775 mr2reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100776 mr2reg = (mr2reg & ~0x07) | pasr;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100777 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
778 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
779 mr2reg = (mr2reg & ~0x80) | (srt << 7);
780 mr2reg |= (odt.rttwr / 60) << 9;
781
782 write_mrreg(ctrl, channel, rank, 2, mr2reg);
783}
784
785static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
786{
787 write_mrreg(ctrl, channel, rank, 3, 0);
788}
789
Angel Pons88521882020-01-05 20:21:20 +0100790void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100791{
792 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100793 int channel;
794
795 FOR_ALL_POPULATED_CHANNELS {
796 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100797 /* MR2 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100798 dram_mr2(ctrl, slotrank, channel);
799
Angel Pons7c49cb82020-03-16 23:17:32 +0100800 /* MR3 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100801 dram_mr3(ctrl, slotrank, channel);
802
Angel Pons7c49cb82020-03-16 23:17:32 +0100803 /* MR1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100804 dram_mr1(ctrl, slotrank, channel);
805
Angel Pons7c49cb82020-03-16 23:17:32 +0100806 /* MR0 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100807 dram_mr0(ctrl, slotrank, channel);
808 }
809 }
810
Angel Pons69e17142020-03-23 12:26:29 +0100811 /* DRAM command NOP (without ODT nor chip selects) */
Angel Ponsca00dec2020-05-02 15:04:00 +0200812 IOSAV_SUBSEQUENCE(BROADCAST_CH, 0,
813 IOSAV_NOP & NO_RANKSEL & ~(0xff << 8),
814 1, 4, 15, SSQ_NA,
815 2, 6, 0, 0,
816 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100817
818 /* DRAM command ZQCL */
Angel Ponsca00dec2020-05-02 15:04:00 +0200819 IOSAV_SUBSEQUENCE(BROADCAST_CH, 1,
820 IOSAV_ZQCS,
821 1, 4, 400, SSQ_NA,
822 1024, 6, 0, 0,
823 ADDR_UPDATE(0, 0, 0, 1, 20, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100824
Angel Pons7c49cb82020-03-16 23:17:32 +0100825 /* Execute command queue on all channels. Do it four times. */
826 MCHBAR32(IOSAV_SEQ_CTL) = (1 << 18) | 4;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100827
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100828 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100829 /* Wait for ref drained */
Angel Pons88521882020-01-05 20:21:20 +0100830 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100831 }
832
Angel Pons7c49cb82020-03-16 23:17:32 +0100833 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +0100834 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100835
836 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +0100837 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100838
Angel Pons88521882020-01-05 20:21:20 +0100839 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100840
841 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
842
Angel Pons7c49cb82020-03-16 23:17:32 +0100843 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100844 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100845
846 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200847 IOSAV_SUBSEQUENCE(channel, 0,
848 IOSAV_ZQCS & NO_RANKSEL,
849 1, 36, 101, SSQ_NA,
850 0, 6, 0, slotrank,
851 ADDR_UPDATE_WRAP(31));
Felix Held9cf1dd22018-07-31 14:52:40 +0200852
Angel Pons7c49cb82020-03-16 23:17:32 +0100853 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100854 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100855
Angel Pons7c49cb82020-03-16 23:17:32 +0100856 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100857 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100858 }
859}
860
Felix Held3b906032020-01-14 17:05:43 +0100861static const u32 lane_base[] = {
862 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
863 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
864 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100865};
866
Angel Pons88521882020-01-05 20:21:20 +0100867void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100868{
Angel Pons88521882020-01-05 20:21:20 +0100869 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100870 int lane;
871 int slotrank, slot;
872 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +0100873 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100874
875 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +0100876 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
877 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100878 }
879
880 for (slot = 0; slot < NUM_SLOTS; slot++)
881 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
882 case 0:
883 default:
Angel Pons88521882020-01-05 20:21:20 +0100884 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100885 break;
886 case 1:
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 + 0].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100889 break;
890 case 2:
Angel Pons88521882020-01-05 20:21:20 +0100891 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100892 ctrl->timings[channel][2 * slot + 1].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100893 break;
894 case 3:
Angel Pons88521882020-01-05 20:21:20 +0100895 pi_coding_ctrl[slot] =
896 (ctrl->timings[channel][2 * slot].pi_coding +
Angel Pons7c49cb82020-03-16 23:17:32 +0100897 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100898 break;
899 }
900
Angel Pons7c49cb82020-03-16 23:17:32 +0100901 /* Enable CMD XOVER */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100902 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons7c49cb82020-03-16 23:17:32 +0100903 reg32 |= (pi_coding_ctrl[0] & 0x3f) << 6;
904 reg32 |= (pi_coding_ctrl[0] & 0x40) << 9;
Angel Pons88521882020-01-05 20:21:20 +0100905 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100906 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
907
Angel Pons88521882020-01-05 20:21:20 +0100908 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100909
Angel Pons7c49cb82020-03-16 23:17:32 +0100910 /* Enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +0100911 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
912 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100913
914 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100915 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Angel Pons88521882020-01-05 20:21:20 +0100916 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100917 if (shift < 0)
918 shift = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100919
Angel Pons88521882020-01-05 20:21:20 +0100920 offset_pi_code = ctrl->pi_code_offset + shift;
Angel Pons7c49cb82020-03-16 23:17:32 +0100921
922 /* Set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +0100923 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
924 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100925 }
926
Angel Pons88521882020-01-05 20:21:20 +0100927 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
928 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100929
Angel Pons88521882020-01-05 20:21:20 +0100930 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +0100931 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100932
Angel Pons88521882020-01-05 20:21:20 +0100933 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100934
935 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100936 int post_timA_min_high = 7, pre_timA_min_high = 7;
937 int post_timA_max_high = 0, pre_timA_max_high = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100938 int shift_402x = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100939 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100940
941 if (shift < 0)
942 shift = 0;
943
944 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +0200945 post_timA_min_high = MIN(post_timA_min_high,
946 (ctrl->timings[channel][slotrank].lanes[lane].
947 timA + shift) >> 6);
948 pre_timA_min_high = MIN(pre_timA_min_high,
949 ctrl->timings[channel][slotrank].lanes[lane].
950 timA >> 6);
951 post_timA_max_high = MAX(post_timA_max_high,
952 (ctrl->timings[channel][slotrank].lanes[lane].
953 timA + shift) >> 6);
954 pre_timA_max_high = MAX(pre_timA_max_high,
955 ctrl->timings[channel][slotrank].lanes[lane].
956 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100957 }
958
959 if (pre_timA_max_high - pre_timA_min_high <
960 post_timA_max_high - post_timA_min_high)
961 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +0100962
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100963 else if (pre_timA_max_high - pre_timA_min_high >
964 post_timA_max_high - post_timA_min_high)
965 shift_402x = -1;
966
Felix Helddee167e2019-12-30 17:30:16 +0100967 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +0100968 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100969 post_timA_min_high) << (4 * slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +0100970
Angel Pons88521882020-01-05 20:21:20 +0100971 reg_roundtrip_latency |=
972 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100973 shift_402x) << (8 * slotrank);
974
975 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +0100976 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100977 (((ctrl->timings[channel][slotrank].lanes[lane].
978 timA + shift) & 0x3f)
979 |
980 ((ctrl->timings[channel][slotrank].lanes[lane].
981 rising + shift) << 8)
982 |
983 (((ctrl->timings[channel][slotrank].lanes[lane].
984 timA + shift -
985 (post_timA_min_high << 6)) & 0x1c0) << 10)
986 | ((ctrl->timings[channel][slotrank].lanes[lane].
987 falling + shift) << 20));
988
Felix Heldfb19c8a2020-01-14 21:27:59 +0100989 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100990 (((ctrl->timings[channel][slotrank].lanes[lane].
991 timC + shift) & 0x3f)
992 |
993 (((ctrl->timings[channel][slotrank].lanes[lane].
994 timB + shift) & 0x3f) << 8)
995 |
996 (((ctrl->timings[channel][slotrank].lanes[lane].
997 timB + shift) & 0x1c0) << 9)
998 |
999 (((ctrl->timings[channel][slotrank].lanes[lane].
1000 timC + shift) & 0x40) << 13));
1001 }
1002 }
Angel Pons88521882020-01-05 20:21:20 +01001003 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1004 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001005}
1006
Angel Pons88521882020-01-05 20:21:20 +01001007static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001008{
Angel Pons88521882020-01-05 20:21:20 +01001009 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001010
1011 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001012 write MR3 MPR enable
1013 in this mode only RD and RDA are allowed
1014 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001015 IOSAV_SUBSEQUENCE(channel, 0,
1016 IOSAV_MRS,
1017 1, 3, ctrl->tMOD, SSQ_NA,
1018 4, 6, 3, slotrank,
1019 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001020
1021 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001022 IOSAV_SUBSEQUENCE(channel, 1,
1023 IOSAV_RD,
1024 1, 3, 4, SSQ_RD,
1025 0, 0, 0, slotrank,
1026 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001027
1028 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001029 IOSAV_SUBSEQUENCE(channel, 2,
1030 IOSAV_RD,
1031 15, 4, ctrl->CAS + 36, SSQ_NA,
1032 0, 6, 0, slotrank,
1033 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001034
1035 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001036 write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001037 IOSAV_SUBSEQUENCE(channel, 3,
1038 IOSAV_MRS,
1039 1, 3, ctrl->tMOD, SSQ_NA,
1040 0, 6, 3, slotrank,
1041 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001042
Angel Pons7c49cb82020-03-16 23:17:32 +01001043 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001044 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001045
Angel Pons88521882020-01-05 20:21:20 +01001046 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001047}
1048
Angel Pons7c49cb82020-03-16 23:17:32 +01001049static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank, int lane)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001050{
1051 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Angel Pons7c49cb82020-03-16 23:17:32 +01001052
1053 return (MCHBAR32(lane_base[lane] +
1054 GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >> (timA % 32)) & 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001055}
1056
1057struct run {
1058 int middle;
1059 int end;
1060 int start;
1061 int all;
1062 int length;
1063};
1064
1065static struct run get_longest_zero_run(int *seq, int sz)
1066{
1067 int i, ls;
1068 int bl = 0, bs = 0;
1069 struct run ret;
1070
1071 ls = 0;
1072 for (i = 0; i < 2 * sz; i++)
1073 if (seq[i % sz]) {
1074 if (i - ls > bl) {
1075 bl = i - ls;
1076 bs = ls;
1077 }
1078 ls = i + 1;
1079 }
1080 if (bl == 0) {
1081 ret.middle = sz / 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001082 ret.start = 0;
1083 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001084 ret.length = sz;
Angel Pons7c49cb82020-03-16 23:17:32 +01001085 ret.all = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001086 return ret;
1087 }
1088
Angel Pons7c49cb82020-03-16 23:17:32 +01001089 ret.start = bs % sz;
1090 ret.end = (bs + bl - 1) % sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001091 ret.middle = (bs + (bl - 1) / 2) % sz;
1092 ret.length = bl;
Angel Pons7c49cb82020-03-16 23:17:32 +01001093 ret.all = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001094
1095 return ret;
1096}
1097
Angel Pons7c49cb82020-03-16 23:17:32 +01001098static void discover_timA_coarse(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001099{
1100 int timA;
1101 int statistics[NUM_LANES][128];
1102 int lane;
1103
1104 for (timA = 0; timA < 128; timA++) {
1105 FOR_ALL_LANES {
1106 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1107 }
1108 program_timings(ctrl, channel);
1109
1110 test_timA(ctrl, channel, slotrank);
1111
1112 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001113 statistics[lane][timA] = !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001114 }
1115 }
1116 FOR_ALL_LANES {
1117 struct run rn = get_longest_zero_run(statistics[lane], 128);
1118 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1119 upperA[lane] = rn.end;
1120 if (upperA[lane] < rn.middle)
1121 upperA[lane] += 128;
Angel Pons7c49cb82020-03-16 23:17:32 +01001122
Patrick Rudolph368b6152016-11-25 16:36:52 +01001123 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001124 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001125 }
1126}
1127
Angel Pons7c49cb82020-03-16 23:17:32 +01001128static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001129{
1130 int timA_delta;
1131 int statistics[NUM_LANES][51];
1132 int lane, i;
1133
1134 memset(statistics, 0, sizeof(statistics));
1135
1136 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01001137
1138 FOR_ALL_LANES {
1139 ctrl->timings[channel][slotrank].lanes[lane].timA
1140 = upperA[lane] + timA_delta + 0x40;
1141 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001142 program_timings(ctrl, channel);
1143
1144 for (i = 0; i < 100; i++) {
1145 test_timA(ctrl, channel, slotrank);
1146 FOR_ALL_LANES {
1147 statistics[lane][timA_delta + 25] +=
Angel Pons7c49cb82020-03-16 23:17:32 +01001148 does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001149 }
1150 }
1151 }
1152 FOR_ALL_LANES {
1153 int last_zero, first_all;
1154
1155 for (last_zero = -25; last_zero <= 25; last_zero++)
1156 if (statistics[lane][last_zero + 25])
1157 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01001158
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001159 last_zero--;
1160 for (first_all = -25; first_all <= 25; first_all++)
1161 if (statistics[lane][first_all + 25] == 100)
1162 break;
1163
Angel Pons7c49cb82020-03-16 23:17:32 +01001164 printram("lane %d: %d, %d\n", lane, last_zero, first_all);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001165
1166 ctrl->timings[channel][slotrank].lanes[lane].timA =
Angel Pons7c49cb82020-03-16 23:17:32 +01001167 (last_zero + first_all) / 2 + upperA[lane];
1168
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001169 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01001170 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001171 }
1172}
1173
Angel Pons891f2bc2020-01-10 01:27:28 +01001174static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001175{
1176 int works[NUM_LANES];
1177 int lane;
Angel Pons7c49cb82020-03-16 23:17:32 +01001178
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001179 while (1) {
1180 int all_works = 1, some_works = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001181
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001182 program_timings(ctrl, channel);
1183 test_timA(ctrl, channel, slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001184
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001185 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001186 works[lane] = !does_lane_work(ctrl, channel, slotrank, lane);
1187
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001188 if (works[lane])
1189 some_works = 1;
1190 else
1191 all_works = 0;
1192 }
1193 if (all_works)
1194 return 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001195
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001196 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001197 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001198 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1199 channel, slotrank);
1200 return MAKE_ERR;
1201 }
Angel Pons88521882020-01-05 20:21:20 +01001202 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001203 printram("4024 -= 2;\n");
1204 continue;
1205 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001206 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001207 printram("4028 += 2;\n");
Angel Pons7c49cb82020-03-16 23:17:32 +01001208
Felix Heldef4fe3e2019-12-31 14:15:05 +01001209 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001210 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1211 channel, slotrank);
1212 return MAKE_ERR;
1213 }
1214 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001215 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001216 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001217 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001218 }
1219 }
1220 return 0;
1221}
1222
1223struct timA_minmax {
1224 int timA_min_high, timA_max_high;
1225};
1226
Angel Pons88521882020-01-05 20:21:20 +01001227static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001228 struct timA_minmax *mnmx)
1229{
1230 int lane;
1231 mnmx->timA_min_high = 7;
1232 mnmx->timA_max_high = 0;
1233
1234 FOR_ALL_LANES {
1235 if (mnmx->timA_min_high >
1236 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1237 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001238 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001239 if (mnmx->timA_max_high <
1240 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1241 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001242 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001243 }
1244}
1245
Angel Pons88521882020-01-05 20:21:20 +01001246static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001247 struct timA_minmax *mnmx)
1248{
1249 struct timA_minmax post;
1250 int shift_402x = 0;
1251
Angel Pons7c49cb82020-03-16 23:17:32 +01001252 /* Get changed maxima */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001253 pre_timA_change(ctrl, channel, slotrank, &post);
1254
1255 if (mnmx->timA_max_high - mnmx->timA_min_high <
1256 post.timA_max_high - post.timA_min_high)
1257 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001258
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001259 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1260 post.timA_max_high - post.timA_min_high)
1261 shift_402x = -1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001262
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001263 else
1264 shift_402x = 0;
1265
Felix Heldef4fe3e2019-12-31 14:15:05 +01001266 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001267 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001268 printram("4024 += %d;\n", shift_402x);
1269 printram("4028 += %d;\n", shift_402x);
1270}
1271
Angel Pons7c49cb82020-03-16 23:17:32 +01001272/*
1273 * Compensate the skew between DQS and DQs.
1274 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001275 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1276 * The controller has to measure and compensate this skew for every byte-lane. By delaying
Angel Pons7c49cb82020-03-16 23:17:32 +01001277 * either all DQ signals or DQS signal, a full phase shift can be introduced. It is assumed
Angel Pons891f2bc2020-01-10 01:27:28 +01001278 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001279 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001280 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1281 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1282 * over all possible values to do a full phase shift and issues read commands. With DQS and
Angel Pons7c49cb82020-03-16 23:17:32 +01001283 * DQ in phase the data being read is expected to alternate on every byte:
1284 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001285 * 0xFF 0x00 0xFF ...
Angel Pons7c49cb82020-03-16 23:17:32 +01001286 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001287 * Once the controller has detected this pattern a bit in the result register is set for the
1288 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001289 */
Angel Pons88521882020-01-05 20:21:20 +01001290int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001291{
1292 int channel, slotrank, lane;
1293 int err;
1294
1295 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1296 int all_high, some_high;
1297 int upperA[NUM_LANES];
1298 struct timA_minmax mnmx;
1299
Angel Pons88521882020-01-05 20:21:20 +01001300 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001301
Felix Held2bb3cdf2018-07-28 00:23:59 +02001302 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001303 IOSAV_SUBSEQUENCE(channel, 0,
1304 IOSAV_PRE,
1305 1, 3, ctrl->tRP, SSQ_NA,
1306 1024, 6, 0, slotrank,
1307 ADDR_UPDATE_NONE);
Felix Held9cf1dd22018-07-31 14:52:40 +02001308
Angel Pons7c49cb82020-03-16 23:17:32 +01001309 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001310 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001311
Angel Pons88521882020-01-05 20:21:20 +01001312 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001313
Felix Heldef4fe3e2019-12-31 14:15:05 +01001314 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001315 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001316 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001317
Felix Held2bb3cdf2018-07-28 00:23:59 +02001318 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001319
Felix Held2bb3cdf2018-07-28 00:23:59 +02001320 all_high = 1;
1321 some_high = 0;
1322 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001323 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001324 some_high = 1;
1325 else
1326 all_high = 0;
1327 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001328
1329 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001330 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001331 printram("4028--;\n");
1332 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001333 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001334 upperA[lane] -= 0x40;
1335
1336 }
1337 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001338 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001339 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001340 printram("4024++;\n");
1341 printram("4028++;\n");
1342 }
1343
1344 program_timings(ctrl, channel);
1345
1346 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1347
1348 err = discover_402x(ctrl, channel, slotrank, upperA);
1349 if (err)
1350 return err;
1351
1352 post_timA_change(ctrl, channel, slotrank, &mnmx);
1353 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1354
1355 discover_timA_fine(ctrl, channel, slotrank, upperA);
1356
1357 post_timA_change(ctrl, channel, slotrank, &mnmx);
1358 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1359
1360 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001361 ctrl->timings[channel][slotrank].lanes[lane].timA -=
1362 mnmx.timA_min_high * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001363 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001364 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001365 printram("4028 -= %d;\n", mnmx.timA_min_high);
1366
1367 post_timA_change(ctrl, channel, slotrank, &mnmx);
1368
1369 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001370 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001371 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001372
1373 printram("final results:\n");
1374 FOR_ALL_LANES
Angel Pons7c49cb82020-03-16 23:17:32 +01001375 printram("Aval: %d, %d, %d: %x\n", channel, slotrank, lane,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001376 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001377
Angel Pons88521882020-01-05 20:21:20 +01001378 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001379
1380 toggle_io_reset();
1381 }
1382
1383 FOR_ALL_POPULATED_CHANNELS {
1384 program_timings(ctrl, channel);
1385 }
1386 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001387 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001388 }
1389 return 0;
1390}
1391
Angel Pons88521882020-01-05 20:21:20 +01001392static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001393{
1394 int lane;
1395
1396 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001397 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1398 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001399 }
1400
Angel Pons88521882020-01-05 20:21:20 +01001401 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001402
1403 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001404 IOSAV_SUBSEQUENCE(channel, 0,
1405 IOSAV_ACT,
1406 4, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
1407 0, 6, 0, slotrank,
1408 ADDR_UPDATE(0, 0, 1, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001409
1410 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001411 IOSAV_SUBSEQUENCE(channel, 1,
1412 IOSAV_NOP,
1413 1, 4, 4, SSQ_WR,
1414 8, 0, 0, slotrank,
1415 ADDR_UPDATE_WRAP(31));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001416
1417 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02001418 IOSAV_SUBSEQUENCE(channel, 2,
1419 IOSAV_WR,
1420 500, 4, 4, SSQ_WR,
1421 0, 0, 0, slotrank,
1422 ADDR_UPDATE(0, 1, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001423
1424 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001425 IOSAV_SUBSEQUENCE(channel, 3,
1426 IOSAV_NOP,
1427 1, 3, ctrl->CWL + ctrl->tWTR + 5, SSQ_WR,
1428 8, 0, 0, slotrank,
1429 ADDR_UPDATE_WRAP(31));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001430
Angel Pons7c49cb82020-03-16 23:17:32 +01001431 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001432 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001433
Angel Pons88521882020-01-05 20:21:20 +01001434 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001435
1436 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001437 IOSAV_SUBSEQUENCE(channel, 0,
1438 IOSAV_PRE,
1439 1, 3, ctrl->tRP, SSQ_NA,
1440 1024, 6, 0, slotrank,
1441 ADDR_UPDATE(0, 0, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001442
1443 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001444 IOSAV_SUBSEQUENCE(channel, 1,
1445 IOSAV_ACT,
1446 8, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->CAS, SSQ_NA,
1447 0, 6, 0, slotrank,
1448 ADDR_UPDATE(0, 0, 1, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001449
1450 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001451 IOSAV_SUBSEQUENCE(channel, 2,
1452 IOSAV_RD,
1453 500, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
1454 0, 0, 0, slotrank,
1455 ADDR_UPDATE(0, 1, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001456
1457 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001458 IOSAV_SUBSEQUENCE(channel, 3,
1459 IOSAV_PRE,
1460 1, 3, ctrl->tRP, SSQ_NA,
1461 1024, 6, 0, slotrank,
1462 ADDR_UPDATE(0, 0, 0, 0, 18, 0, 0, 0));
Felix Held9cf1dd22018-07-31 14:52:40 +02001463
Angel Pons7c49cb82020-03-16 23:17:32 +01001464 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001465 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001466
Angel Pons88521882020-01-05 20:21:20 +01001467 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001468}
1469
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001470static void timC_threshold_process(int *data, const int count)
1471{
1472 int min = data[0];
1473 int max = min;
1474 int i;
1475 for (i = 1; i < count; i++) {
1476 if (min > data[i])
1477 min = data[i];
Angel Pons7c49cb82020-03-16 23:17:32 +01001478
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001479 if (max < data[i])
1480 max = data[i];
1481 }
Angel Pons7c49cb82020-03-16 23:17:32 +01001482 int threshold = min / 2 + max / 2;
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001483 for (i = 0; i < count; i++)
1484 data[i] = data[i] > threshold;
Angel Pons7c49cb82020-03-16 23:17:32 +01001485
Angel Pons891f2bc2020-01-10 01:27:28 +01001486 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001487}
1488
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001489static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1490{
1491 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01001492 int stats[NUM_LANES][MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001493 int lane;
1494
Angel Pons88521882020-01-05 20:21:20 +01001495 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001496
1497 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001498 IOSAV_SUBSEQUENCE(channel, 0,
1499 IOSAV_PRE,
1500 1, 3, ctrl->tRP, SSQ_NA,
1501 1024, 6, 0, slotrank,
1502 ADDR_UPDATE(0, 0, 0, 0, 18, 0, 0, 0));
Felix Held9cf1dd22018-07-31 14:52:40 +02001503
Angel Pons7c49cb82020-03-16 23:17:32 +01001504 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001505 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001506
1507 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001508 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001509 program_timings(ctrl, channel);
1510
1511 test_timC(ctrl, channel, slotrank);
1512
1513 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001514 stats[lane][timC] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001515 }
1516 }
1517 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001518 struct run rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1519
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001520 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001521 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1522 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001523 /*
1524 * With command training not being done yet, the lane can be erroneous.
1525 * Take the average as reference and try again to find a run.
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001526 */
Angel Pons7c49cb82020-03-16 23:17:32 +01001527 timC_threshold_process(stats[lane], ARRAY_SIZE(stats[lane]));
1528 rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1529
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001530 if (rn.all || rn.length < 8) {
1531 printk(BIOS_EMERG, "timC recovery failed\n");
1532 return MAKE_ERR;
1533 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001534 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001535 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001536 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001537 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001538 }
1539 return 0;
1540}
1541
Angel Pons88521882020-01-05 20:21:20 +01001542static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001543{
1544 int channel, ret = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001545
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001546 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1547 ret++;
Angel Pons7c49cb82020-03-16 23:17:32 +01001548
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001549 return ret;
1550}
1551
Angel Pons88521882020-01-05 20:21:20 +01001552static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001553{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301554 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001555 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Angel Pons7c49cb82020-03-16 23:17:32 +01001556
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001557 for (j = 0; j < 16; j++)
1558 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
Angel Pons7c49cb82020-03-16 23:17:32 +01001559
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001560 sfence();
1561}
1562
Angel Pons88521882020-01-05 20:21:20 +01001563static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001564{
1565 int ret = 0;
1566 int channel;
1567 FOR_ALL_POPULATED_CHANNELS ret++;
1568 return ret;
1569}
1570
Angel Pons88521882020-01-05 20:21:20 +01001571static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001572{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301573 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001574 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301575 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01001576
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001577 for (j = 0; j < 16; j++)
1578 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
Angel Pons7c49cb82020-03-16 23:17:32 +01001579
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001580 for (j = 0; j < 16; j++)
1581 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
Angel Pons7c49cb82020-03-16 23:17:32 +01001582
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001583 sfence();
1584}
1585
Angel Pons88521882020-01-05 20:21:20 +01001586static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001587{
1588 int channel, slotrank, lane;
1589
1590 FOR_ALL_POPULATED_CHANNELS {
1591 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001592 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1593 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001594 }
1595
1596 program_timings(ctrl, channel);
1597
1598 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001599 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001600
1601 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001602 write MR3 MPR enable
1603 in this mode only RD and RDA are allowed
1604 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001605 IOSAV_SUBSEQUENCE(channel, 0,
1606 IOSAV_MRS,
1607 1, 3, ctrl->tMOD, SSQ_NA,
1608 4, 6, 3, slotrank,
1609 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001610
1611 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001612 IOSAV_SUBSEQUENCE(channel, 1,
1613 IOSAV_RD,
1614 3, 4, 4, SSQ_RD,
1615 0, 0, 0, slotrank,
1616 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001617
1618 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001619 IOSAV_SUBSEQUENCE(channel, 2,
1620 IOSAV_RD,
1621 1, 4, ctrl->CAS + 8, SSQ_NA,
1622 0, 6, 0, slotrank,
1623 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001624
1625 /* DRAM command MRS
1626 * write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001627 IOSAV_SUBSEQUENCE(channel, 3,
1628 IOSAV_MRS,
1629 1, 3, ctrl->tMOD, SSQ_NA,
1630 0, 6, 3, slotrank,
1631 ADDR_UPDATE_NONE);
Felix Held9cf1dd22018-07-31 14:52:40 +02001632
Angel Pons7c49cb82020-03-16 23:17:32 +01001633 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001634 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001635
Angel Pons88521882020-01-05 20:21:20 +01001636 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001637 }
1638
1639 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001640 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1641 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001642 }
1643
1644 program_timings(ctrl, channel);
1645
1646 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001647 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001648 /* DRAM command MRS
1649 * write MR3 MPR enable
1650 * in this mode only RD and RDA are allowed
1651 * all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001652 IOSAV_SUBSEQUENCE(channel, 0,
1653 IOSAV_MRS,
1654 1, 3, ctrl->tMOD, SSQ_NA,
1655 4, 6, 3, slotrank,
1656 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001657
1658 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001659 IOSAV_SUBSEQUENCE(channel, 1,
1660 IOSAV_RD,
1661 3, 4, 4, SSQ_RD,
1662 0, 0, 0, slotrank,
1663 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001664
1665 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001666 IOSAV_SUBSEQUENCE(channel, 2,
1667 IOSAV_RD,
1668 1, 4, ctrl->CAS + 8, SSQ_NA,
1669 0, 6, 0, slotrank,
1670 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001671
1672 /* DRAM command MRS
1673 * write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001674 IOSAV_SUBSEQUENCE(channel, 3,
1675 IOSAV_MRS,
1676 1, 3, ctrl->tMOD, SSQ_NA,
1677 0, 6, 3, slotrank,
1678 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001679
Angel Pons7c49cb82020-03-16 23:17:32 +01001680 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001681 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001682
Angel Pons88521882020-01-05 20:21:20 +01001683 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001684 }
1685 }
1686}
1687
Angel Pons88521882020-01-05 20:21:20 +01001688static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001689{
1690 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001691 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001692
Angel Pons88521882020-01-05 20:21:20 +01001693 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001694 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001695 IOSAV_SUBSEQUENCE(channel, 0,
1696 IOSAV_NOP,
1697 1, 3, ctrl->CWL + ctrl->tWLO, SSQ_WR,
1698 8, 0, 0, slotrank,
1699 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001700
1701 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001702 IOSAV_SUBSEQUENCE(channel, 1,
1703 IOSAV_NOP_ALT,
1704 1, 3, ctrl->CAS + 38, SSQ_RD,
1705 4, 0, 0, slotrank,
1706 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001707
Angel Pons7c49cb82020-03-16 23:17:32 +01001708 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001709 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001710
Angel Pons88521882020-01-05 20:21:20 +01001711 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001712
1713 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001714 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001715}
1716
1717static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1718{
1719 int timB;
1720 int statistics[NUM_LANES][128];
1721 int lane;
1722
Angel Pons88521882020-01-05 20:21:20 +01001723 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001724
1725 for (timB = 0; timB < 128; timB++) {
1726 FOR_ALL_LANES {
1727 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1728 }
1729 program_timings(ctrl, channel);
1730
1731 test_timB(ctrl, channel, slotrank);
1732
1733 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001734 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1735 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1736 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001737 }
1738 }
1739 FOR_ALL_LANES {
1740 struct run rn = get_longest_zero_run(statistics[lane], 128);
Angel Pons7c49cb82020-03-16 23:17:32 +01001741 /*
1742 * timC is a direct function of timB's 6 LSBs. Some tests increments the value
1743 * of timB by a small value, which might cause the 6-bit value to overflow if
1744 * it's close to 0x3f. Increment the value by a small offset if it's likely
1745 * to overflow, to make sure it won't overflow while running tests and bricks
1746 * the system due to a non matching timC.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001747 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001748 * TODO: find out why some tests (edge write discovery) increment timB.
1749 */
1750 if ((rn.start & 0x3f) == 0x3e)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001751 rn.start += 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001752 else if ((rn.start & 0x3f) == 0x3f)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001753 rn.start += 1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001754
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001755 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1756 if (rn.all) {
1757 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1758 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001759
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001760 return MAKE_ERR;
1761 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001762 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1763 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001764 }
1765 return 0;
1766}
1767
1768static int get_timB_high_adjust(u64 val)
1769{
1770 int i;
1771
1772 /* good */
1773 if (val == 0xffffffffffffffffLL)
1774 return 0;
1775
1776 if (val >= 0xf000000000000000LL) {
1777 /* needs negative adjustment */
1778 for (i = 0; i < 8; i++)
1779 if (val << (8 * (7 - i) + 4))
1780 return -i;
1781 } else {
1782 /* needs positive adjustment */
1783 for (i = 0; i < 8; i++)
1784 if (val >> (8 * (7 - i) + 4))
1785 return i;
1786 }
1787 return 8;
1788}
1789
Angel Pons88521882020-01-05 20:21:20 +01001790static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001791{
1792 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001793 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001794 FOR_ALL_POPULATED_CHANNELS {
1795 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001796 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001797 }
1798 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1799
Angel Pons88521882020-01-05 20:21:20 +01001800 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001801
Angel Pons88521882020-01-05 20:21:20 +01001802 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001803
1804 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001805 IOSAV_SUBSEQUENCE(channel, 0,
1806 IOSAV_ACT,
1807 1, 3, ctrl->tRCD, SSQ_NA,
1808 0, 6, 0, slotrank,
1809 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001810
1811 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001812 IOSAV_SUBSEQUENCE(channel, 1,
1813 IOSAV_NOP,
1814 1, 3, 4, SSQ_WR,
1815 8, 0, 0, slotrank,
1816 ADDR_UPDATE_WRAP(31));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001817
1818 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02001819 IOSAV_SUBSEQUENCE(channel, 2,
1820 IOSAV_WR,
1821 3, 4, 4, SSQ_WR,
1822 0, 0, 0, slotrank,
1823 ADDR_UPDATE(0, 1, 0, 0, 31, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001824
1825 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001826 IOSAV_SUBSEQUENCE(channel, 3,
1827 IOSAV_NOP,
1828 1, 3, ctrl->CWL + ctrl->tWTR + 5, SSQ_WR,
1829 8, 0, 0, slotrank,
1830 ADDR_UPDATE_WRAP(31));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001831
Angel Pons7c49cb82020-03-16 23:17:32 +01001832 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001833 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001834
Angel Pons88521882020-01-05 20:21:20 +01001835 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001836
1837 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001838 IOSAV_SUBSEQUENCE(channel, 0,
1839 IOSAV_PRE,
1840 1, 3, ctrl->tRP, SSQ_NA,
1841 1024, 6, 0, slotrank,
1842 ADDR_UPDATE(0, 0, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001843
1844 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001845 IOSAV_SUBSEQUENCE(channel, 1,
1846 IOSAV_ACT,
1847 1, 3, ctrl->tRCD, SSQ_NA,
1848 0, 6, 0, slotrank,
1849 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001850
1851 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001852 IOSAV_SUBSEQUENCE(channel, 2,
1853 IOSAV_RD | (3 << 16),
1854 1, 3, ctrl->tRP +
1855 ctrl->timings[channel][slotrank].roundtrip_latency +
1856 ctrl->timings[channel][slotrank].io_latency, SSQ_RD,
1857 8, 6, 0, slotrank,
1858 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001859
Angel Pons7c49cb82020-03-16 23:17:32 +01001860 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001861 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Felix Held9cf1dd22018-07-31 14:52:40 +02001862
Angel Pons88521882020-01-05 20:21:20 +01001863 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001864 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001865 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01001866 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01001867 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001868 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
1869 ctrl->timings[channel][slotrank].lanes[lane].timB +=
1870 get_timB_high_adjust(res) * 64;
1871
1872 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01001873 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
1874 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001875 }
1876 }
Angel Pons88521882020-01-05 20:21:20 +01001877 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001878}
1879
Angel Pons88521882020-01-05 20:21:20 +01001880static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001881{
1882 int slotrank;
1883
Angel Pons88521882020-01-05 20:21:20 +01001884 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001885
1886 /* choose an existing rank. */
1887 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
1888
Angel Pons69e17142020-03-23 12:26:29 +01001889 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02001890 IOSAV_SUBSEQUENCE(channel, 0,
1891 IOSAV_ZQCS & NO_RANKSEL,
1892 1, 4, 4, SSQ_NA,
1893 0, 6, 0, slotrank,
1894 ADDR_UPDATE_WRAP(31));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001895
Angel Pons7c49cb82020-03-16 23:17:32 +01001896 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001897 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001898
Angel Pons88521882020-01-05 20:21:20 +01001899 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001900}
1901
Angel Pons7c49cb82020-03-16 23:17:32 +01001902/*
1903 * Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001904 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001905 * Since DDR3 uses a fly-by topology, the data and strobes signals reach the chips at different
1906 * times with respect to command, address and clock signals. By delaying either all DQ/DQS or
1907 * all CMD/ADDR/CLK signals, a full phase shift can be introduced. It is assumed that the
1908 * CLK/ADDR/CMD signals have the same routing delay.
1909 *
1910 * To find the required phase shift the DRAM is placed in "write leveling" mode. In this mode,
1911 * the DRAM-chip samples the CLK on every DQS edge and feeds back the sampled value on the data
1912 * lanes (DQ).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001913 */
Angel Pons88521882020-01-05 20:21:20 +01001914int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001915{
1916 int channel, slotrank, lane;
1917 int err;
1918
1919 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001920 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001921
1922 FOR_ALL_POPULATED_CHANNELS {
1923 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001924 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001925 }
1926
Angel Pons7c49cb82020-03-16 23:17:32 +01001927 /* Refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01001928 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001929 FOR_ALL_POPULATED_CHANNELS {
1930 write_op(ctrl, channel);
1931 }
1932
Angel Pons7c49cb82020-03-16 23:17:32 +01001933 /* Enable write leveling on all ranks
1934 Disable all DQ outputs
1935 Only NOP is allowed in this mode */
1936 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
1937 write_mrreg(ctrl, channel, slotrank, 1,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001938 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001939
Angel Pons88521882020-01-05 20:21:20 +01001940 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001941
1942 toggle_io_reset();
1943
Angel Pons7c49cb82020-03-16 23:17:32 +01001944 /* Set any valid value for timB, it gets corrected later */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001945 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1946 err = discover_timB(ctrl, channel, slotrank);
1947 if (err)
1948 return err;
1949 }
1950
Angel Pons7c49cb82020-03-16 23:17:32 +01001951 /* Disable write leveling on all ranks */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001952 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
Angel Pons7c49cb82020-03-16 23:17:32 +01001953 write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001954
Angel Pons88521882020-01-05 20:21:20 +01001955 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001956
1957 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001958 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001959
Angel Pons7c49cb82020-03-16 23:17:32 +01001960 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01001961 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001962
1963 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001964 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
1965 MCHBAR32(IOSAV_STATUS_ch(channel));
1966 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001967
1968 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02001969 IOSAV_SUBSEQUENCE(channel, 0,
1970 IOSAV_ZQCS & NO_RANKSEL,
1971 1, 36, 101, SSQ_NA,
1972 0, 6, 0, 0,
1973 ADDR_UPDATE_WRAP(31));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001974
Angel Pons7c49cb82020-03-16 23:17:32 +01001975 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001976 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001977
Angel Pons88521882020-01-05 20:21:20 +01001978 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001979 }
1980
1981 toggle_io_reset();
1982
1983 printram("CPE\n");
1984 precharge(ctrl);
1985 printram("CPF\n");
1986
1987 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001988 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001989 }
1990
1991 FOR_ALL_POPULATED_CHANNELS {
1992 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01001993 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001994 }
1995
1996 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1997 err = discover_timC(ctrl, channel, slotrank);
1998 if (err)
1999 return err;
2000 }
2001
2002 FOR_ALL_POPULATED_CHANNELS
2003 program_timings(ctrl, channel);
2004
2005 /* measure and adjust timB timings */
2006 adjust_high_timB(ctrl);
2007
2008 FOR_ALL_POPULATED_CHANNELS
2009 program_timings(ctrl, channel);
2010
2011 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002012 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002013 }
2014 return 0;
2015}
2016
Angel Pons88521882020-01-05 20:21:20 +01002017static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002018{
2019 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2020 int timC_delta;
2021 int lanes_ok = 0;
2022 int ctr = 0;
2023 int lane;
2024
2025 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2026 FOR_ALL_LANES {
2027 ctrl->timings[channel][slotrank].lanes[lane].timC =
2028 saved_rt.lanes[lane].timC + timC_delta;
2029 }
2030 program_timings(ctrl, channel);
2031 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002032 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002033 }
2034
Angel Pons88521882020-01-05 20:21:20 +01002035 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002036
Angel Pons88521882020-01-05 20:21:20 +01002037 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002038 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002039 IOSAV_SUBSEQUENCE(channel, 0,
2040 IOSAV_ACT,
2041 8, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
2042 ctr, 6, 0, slotrank,
2043 ADDR_UPDATE(0, 0, 1, 0, 18, 0, 0, 0));
Felix Held9fe248f2018-07-31 20:59:45 +02002044
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002045 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002046 IOSAV_SUBSEQUENCE(channel, 1,
2047 IOSAV_WR,
2048 32, 4, ctrl->CWL + ctrl->tWTR + 8, SSQ_WR,
2049 0, 0, 0, slotrank,
2050 ADDR_UPDATE(0, 1, 0, 0, 18, 3, 0, 2));
2051
Angel Ponsc36cd072020-05-02 16:51:39 +02002052 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002053
2054 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002055 IOSAV_SUBSEQUENCE(channel, 2,
2056 IOSAV_RD,
2057 32, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
2058 0, 0, 0, slotrank,
2059 ADDR_UPDATE(0, 1, 0, 0, 18, 3, 0, 2));
2060
Angel Ponsc36cd072020-05-02 16:51:39 +02002061 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002062
2063 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002064 IOSAV_SUBSEQUENCE(channel, 3,
2065 IOSAV_PRE,
2066 1, 4, 15, SSQ_NA,
2067 1024, 6, 0, slotrank,
2068 ADDR_UPDATE(0, 0, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002069
Angel Pons7c49cb82020-03-16 23:17:32 +01002070 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002071 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002072
Angel Pons88521882020-01-05 20:21:20 +01002073 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002074 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002075 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002076
2077 if (r32 == 0)
2078 lanes_ok |= 1 << lane;
2079 }
2080 ctr++;
Patrick Rudolphdd662872017-10-28 18:20:11 +02002081 if (lanes_ok == ((1 << ctrl->lanes) - 1))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002082 break;
2083 }
2084
2085 ctrl->timings[channel][slotrank] = saved_rt;
2086
Patrick Rudolphdd662872017-10-28 18:20:11 +02002087 return lanes_ok != ((1 << ctrl->lanes) - 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002088}
2089
Angel Pons88521882020-01-05 20:21:20 +01002090static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002091{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302092 unsigned int i, j;
Angel Pons7c49cb82020-03-16 23:17:32 +01002093 unsigned int offset = get_precedening_channels(ctrl, channel) * 0x40;
2094 unsigned int step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002095
2096 if (patno) {
2097 u8 base8 = 0x80 >> ((patno - 1) % 8);
2098 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2099 for (i = 0; i < 32; i++) {
2100 for (j = 0; j < 16; j++) {
2101 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01002102
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002103 if (invert[patno - 1][i] & (1 << (j / 2)))
2104 val = ~val;
Angel Pons7c49cb82020-03-16 23:17:32 +01002105
2106 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002107 }
2108 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002109 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002110 for (i = 0; i < ARRAY_SIZE(pattern); i++) {
2111 for (j = 0; j < 16; j++) {
2112 const u32 val = pattern[i][j];
2113 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
2114 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002115 }
2116 sfence();
2117 }
2118}
2119
Angel Pons88521882020-01-05 20:21:20 +01002120static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002121{
2122 int channel, slotrank;
2123
2124 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002125 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002126
Angel Pons7c49cb82020-03-16 23:17:32 +01002127 /* Choose an existing rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002128 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2129
2130 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02002131 IOSAV_SUBSEQUENCE(channel, 0,
2132 IOSAV_ZQCS & NO_RANKSEL,
2133 1, 4, 4, SSQ_NA,
2134 0, 6, 0, slotrank,
2135 ADDR_UPDATE_WRAP(31));
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);
2141 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002142 }
2143
2144 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002145 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002146 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002147 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002148
2149 /* choose an existing rank. */
2150 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2151
2152 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02002153 IOSAV_SUBSEQUENCE(channel, 0,
2154 IOSAV_ZQCS & NO_RANKSEL,
2155 1, 4, 4, SSQ_NA,
2156 0, 6, 0, slotrank,
2157 ADDR_UPDATE_WRAP(31));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002158
Angel Pons7c49cb82020-03-16 23:17:32 +01002159 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002160 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002161
Angel Pons88521882020-01-05 20:21:20 +01002162 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002163 }
2164
Angel Pons7c49cb82020-03-16 23:17:32 +01002165 /* JEDEC reset */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002166 dram_jedecreset(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01002167
2168 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002169 dram_mrscommands(ctrl);
2170
2171 toggle_io_reset();
2172}
2173
2174#define MIN_C320C_LEN 13
2175
2176static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2177{
2178 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2179 int slotrank;
2180 int c320c;
2181 int stat[NUM_SLOTRANKS][256];
2182 int delta = 0;
2183
2184 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2185
2186 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002187 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002188 }
2189
2190 ctrl->cmd_stretch[channel] = cmd_stretch;
2191
Angel Pons88521882020-01-05 20:21:20 +01002192 MCHBAR32(TC_RAP_ch(channel)) =
Angel Pons7c49cb82020-03-16 23:17:32 +01002193 (ctrl->tRRD << 0)
2194 | (ctrl->tRTP << 4)
2195 | (ctrl->tCKE << 8)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002196 | (ctrl->tWTR << 12)
2197 | (ctrl->tFAW << 16)
Angel Pons7c49cb82020-03-16 23:17:32 +01002198 | (ctrl->tWR << 24)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002199 | (ctrl->cmd_stretch[channel] << 30);
2200
2201 if (ctrl->cmd_stretch[channel] == 2)
2202 delta = 2;
2203 else if (ctrl->cmd_stretch[channel] == 0)
2204 delta = 4;
2205
2206 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002207 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002208 }
2209
2210 for (c320c = -127; c320c <= 127; c320c++) {
2211 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002212 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002213 }
2214 program_timings(ctrl, channel);
2215 reprogram_320c(ctrl);
2216 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002217 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002218 }
2219 }
2220 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002221 struct run rn = get_longest_zero_run(stat[slotrank], 255);
2222
Angel Pons88521882020-01-05 20:21:20 +01002223 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002224 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2225 channel, slotrank, rn.start, rn.middle, rn.end);
Angel Pons7c49cb82020-03-16 23:17:32 +01002226
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002227 if (rn.all || rn.length < MIN_C320C_LEN) {
2228 FOR_ALL_POPULATED_RANKS {
2229 ctrl->timings[channel][slotrank] =
2230 saved_timings[channel][slotrank];
2231 }
2232 return MAKE_ERR;
2233 }
2234 }
2235
2236 return 0;
2237}
2238
Angel Pons7c49cb82020-03-16 23:17:32 +01002239/*
2240 * Adjust CMD phase shift and try multiple command rates.
2241 * A command rate of 2T doubles the time needed for address and command decode.
2242 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002243int command_training(ramctr_timing *ctrl)
2244{
2245 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002246
2247 FOR_ALL_POPULATED_CHANNELS {
2248 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002249 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002250 }
2251
2252 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002253 int cmdrate, err;
2254
2255 /*
2256 * Dual DIMM per channel:
Angel Pons7c49cb82020-03-16 23:17:32 +01002257 * Issue:
2258 * While c320c discovery seems to succeed raminit will fail in write training.
2259 *
2260 * Workaround:
2261 * Skip 1T in dual DIMM mode, that's only supported by a few DIMMs.
2262 * Only try 1T mode for XMP DIMMs that request it in dual DIMM mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002263 *
2264 * Single DIMM per channel:
2265 * Try command rate 1T and 2T
2266 */
2267 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002268 if (ctrl->tCMD)
2269 /* XMP gives the CMD rate in clock ticks, not ns */
2270 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002271
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002272 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002273 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2274
2275 if (!err)
2276 break;
2277 }
2278
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002279 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002280 printk(BIOS_EMERG, "c320c discovery failed\n");
2281 return err;
2282 }
2283
Angel Pons891f2bc2020-01-10 01:27:28 +01002284 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002285 }
2286
2287 FOR_ALL_POPULATED_CHANNELS
2288 program_timings(ctrl, channel);
2289
2290 reprogram_320c(ctrl);
2291 return 0;
2292}
2293
Angel Pons891f2bc2020-01-10 01:27:28 +01002294static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002295{
2296 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002297 int stats[NUM_LANES][MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002298 int lane;
2299
2300 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2301 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002302 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
Angel Pons891f2bc2020-01-10 01:27:28 +01002303 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002304 }
2305 program_timings(ctrl, channel);
2306
2307 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002308 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2309 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002310 }
2311
Angel Pons88521882020-01-05 20:21:20 +01002312 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002313
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002314 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002315 write MR3 MPR enable
2316 in this mode only RD and RDA are allowed
2317 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002318 IOSAV_SUBSEQUENCE(channel, 0,
2319 IOSAV_MRS,
2320 1, 3, ctrl->tMOD, SSQ_NA,
2321 4, 6, 3, slotrank,
2322 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002323
2324 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002325 IOSAV_SUBSEQUENCE(channel, 1,
2326 IOSAV_RD,
2327 500, 4, 4, SSQ_RD,
2328 0, 0, 0, slotrank,
2329 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002330
2331 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002332 IOSAV_SUBSEQUENCE(channel, 2,
2333 IOSAV_RD,
2334 1, 4, ctrl->CAS + 8, SSQ_NA,
2335 0, 6, 0, slotrank,
2336 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002337
2338 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002339 MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002340 IOSAV_SUBSEQUENCE(channel, 3,
2341 IOSAV_MRS,
2342 1, 3, ctrl->tMOD, SSQ_NA,
2343 0, 6, 3, slotrank,
2344 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002345
Angel Pons7c49cb82020-03-16 23:17:32 +01002346 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002347 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002348
Angel Pons88521882020-01-05 20:21:20 +01002349 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002350
2351 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002352 stats[lane][edge] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002353 }
2354 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002355
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002356 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002357 struct run rn = get_longest_zero_run(stats[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002358 edges[lane] = rn.middle;
Angel Pons7c49cb82020-03-16 23:17:32 +01002359
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002360 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002361 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n", channel,
2362 slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002363 return MAKE_ERR;
2364 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002365 printram("eval %d, %d, %d: %02x\n", channel, slotrank, lane, edges[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002366 }
2367 return 0;
2368}
2369
2370int discover_edges(ramctr_timing *ctrl)
2371{
2372 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2373 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2374 int channel, slotrank, lane;
2375 int err;
2376
Angel Pons88521882020-01-05 20:21:20 +01002377 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002378
2379 toggle_io_reset();
2380
2381 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002382 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002383 }
2384
2385 FOR_ALL_POPULATED_CHANNELS {
2386 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002387 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002388 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002389 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002390 }
2391
2392 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002393 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2394 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002395 }
2396
2397 program_timings(ctrl, channel);
2398
2399 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002400 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002401
2402 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002403 MR3 enable MPR
2404 write MR3 MPR enable
2405 in this mode only RD and RDA are allowed
2406 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002407 IOSAV_SUBSEQUENCE(channel, 0,
2408 IOSAV_MRS,
2409 1, 3, ctrl->tMOD, SSQ_NA,
2410 4, 6, 3, slotrank,
2411 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002412
2413 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002414 IOSAV_SUBSEQUENCE(channel, 1,
2415 IOSAV_RD,
2416 3, 4, 4, SSQ_RD,
2417 0, 0, 0, slotrank,
2418 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002419
2420 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002421 IOSAV_SUBSEQUENCE(channel, 2,
2422 IOSAV_RD,
2423 1, 4, ctrl->CAS + 8, SSQ_NA,
2424 0, 6, 0, slotrank,
2425 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002426
2427 /* DRAM command MRS
2428 * MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002429 IOSAV_SUBSEQUENCE(channel, 3,
2430 IOSAV_MRS,
2431 1, 3, ctrl->tMOD, SSQ_NA,
2432 0, 6, 3, slotrank,
2433 ADDR_UPDATE_NONE);
Felix Held9cf1dd22018-07-31 14:52:40 +02002434
Angel Pons7c49cb82020-03-16 23:17:32 +01002435 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002436 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002437
Angel Pons88521882020-01-05 20:21:20 +01002438 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002439 }
2440
2441 /* XXX: check any measured value ? */
2442
2443 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002444 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
Angel Pons7c49cb82020-03-16 23:17:32 +01002445 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002446 }
2447
2448 program_timings(ctrl, channel);
2449
2450 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002451 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002452
2453 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002454 MR3 enable MPR
2455 write MR3 MPR enable
2456 in this mode only RD and RDA are allowed
2457 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002458 IOSAV_SUBSEQUENCE(channel, 0,
2459 IOSAV_MRS,
2460 1, 3, ctrl->tMOD, SSQ_NA,
2461 4, 6, 3, slotrank,
2462 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002463
2464 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002465 IOSAV_SUBSEQUENCE(channel, 1,
2466 IOSAV_RD,
2467 3, 4, 4, SSQ_RD,
2468 0, 0, 0, slotrank,
2469 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002470
2471 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002472 IOSAV_SUBSEQUENCE(channel, 2,
2473 IOSAV_RD,
2474 1, 4, ctrl->CAS + 8, SSQ_NA,
2475 0, 6, 0, slotrank,
2476 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002477
2478 /* DRAM command MRS
2479 * MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002480 IOSAV_SUBSEQUENCE(channel, 3,
2481 IOSAV_MRS,
2482 1, 3, ctrl->tMOD, SSQ_NA,
2483 0, 6, 3, slotrank,
2484 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002485
Angel Pons7c49cb82020-03-16 23:17:32 +01002486 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002487 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002488
Angel Pons88521882020-01-05 20:21:20 +01002489 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002490 }
2491
2492 /* XXX: check any measured value ? */
2493
2494 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002495 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002496 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002497 }
2498
2499 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002500 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002501 }
2502
Angel Pons0c3936e2020-03-22 12:49:27 +01002503 /*
2504 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2505 * also use a single loop. It would seem that it is a debugging configuration.
2506 */
Angel Pons88521882020-01-05 20:21:20 +01002507 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2508 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002509
2510 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2511 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002512 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002513 if (err)
2514 return err;
2515 }
2516
Angel Pons88521882020-01-05 20:21:20 +01002517 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2518 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002519
2520 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2521 err = discover_edges_real(ctrl, channel, slotrank,
2522 rising_edges[channel][slotrank]);
2523 if (err)
2524 return err;
2525 }
2526
Angel Pons88521882020-01-05 20:21:20 +01002527 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002528
2529 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2530 ctrl->timings[channel][slotrank].lanes[lane].falling =
2531 falling_edges[channel][slotrank][lane];
2532 ctrl->timings[channel][slotrank].lanes[lane].rising =
2533 rising_edges[channel][slotrank][lane];
2534 }
2535
2536 FOR_ALL_POPULATED_CHANNELS {
2537 program_timings(ctrl, channel);
2538 }
2539
2540 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002541 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002542 }
2543 return 0;
2544}
2545
Angel Pons7c49cb82020-03-16 23:17:32 +01002546static int discover_edges_write_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002547{
2548 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002549 u32 raw_stats[MAX_EDGE_TIMING + 1];
2550 int stats[MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002551 const int reg3000b24[] = { 0, 0xc, 0x2c };
2552 int lane, i;
2553 int lower[NUM_LANES];
2554 int upper[NUM_LANES];
2555 int pat;
2556
2557 FOR_ALL_LANES {
2558 lower[lane] = 0;
2559 upper[lane] = MAX_EDGE_TIMING;
2560 }
2561
2562 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002563 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002564 printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
2565
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002566 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2567 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002568 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002569 printram("using pattern %d\n", pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002570
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002571 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2572 FOR_ALL_LANES {
2573 ctrl->timings[channel][slotrank].lanes[lane].
2574 rising = edge;
2575 ctrl->timings[channel][slotrank].lanes[lane].
2576 falling = edge;
2577 }
2578 program_timings(ctrl, channel);
2579
2580 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002581 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2582 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002583 }
Angel Pons88521882020-01-05 20:21:20 +01002584 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002585
2586 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002587 IOSAV_SUBSEQUENCE(channel, 0,
2588 IOSAV_ACT,
2589 4, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
2590 0, 6, 0, slotrank,
2591 ADDR_UPDATE(0, 0, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002592
2593 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002594 IOSAV_SUBSEQUENCE(channel, 1,
2595 IOSAV_WR,
2596 32, 20, ctrl->tWTR + ctrl->CWL + 8, SSQ_WR,
2597 0, 0, 0, slotrank,
2598 ADDR_UPDATE(0, 1, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002599
2600 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002601 IOSAV_SUBSEQUENCE(channel, 2,
2602 IOSAV_RD,
2603 32, 20, MAX(ctrl->tRTP, 8), SSQ_RD,
2604 0, 0, 0, slotrank,
2605 ADDR_UPDATE(0, 1, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002606
2607 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002608 IOSAV_SUBSEQUENCE(channel, 3,
2609 IOSAV_PRE,
2610 1, 3, ctrl->tRP, SSQ_NA,
2611 1024, 6, 0, slotrank,
2612 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002613
Angel Pons7c49cb82020-03-16 23:17:32 +01002614 /* Execute command queue */
2615 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002616
Angel Pons88521882020-01-05 20:21:20 +01002617 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002618 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002619 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002620 }
2621
Angel Pons7c49cb82020-03-16 23:17:32 +01002622 /* FIXME: This register only exists on Ivy Bridge */
Angel Pons098240eb2020-03-22 12:55:32 +01002623 raw_stats[edge] = MCHBAR32(IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002624 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002625
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002626 FOR_ALL_LANES {
2627 struct run rn;
2628 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
Angel Pons7c49cb82020-03-16 23:17:32 +01002629 stats[edge] = !!(raw_stats[edge] & (1 << lane));
2630
2631 rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
2632
2633 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, "
2634 "0x%02x-0x%02x\n", channel, slotrank, i, rn.start,
2635 rn.middle, rn.end, rn.start + ctrl->edge_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002636 rn.end - ctrl->edge_offset[i]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002637
2638 lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
2639 upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
2640
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002641 edges[lane] = (lower[lane] + upper[lane]) / 2;
2642 if (rn.all || (lower[lane] > upper[lane])) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002643 printk(BIOS_EMERG, "edge write discovery failed: "
2644 "%d, %d, %d\n", channel, slotrank, lane);
2645
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002646 return MAKE_ERR;
2647 }
2648 }
2649 }
2650 }
2651
Angel Pons88521882020-01-05 20:21:20 +01002652 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002653 printram("CPA\n");
2654 return 0;
2655}
2656
2657int discover_edges_write(ramctr_timing *ctrl)
2658{
2659 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
Angel Pons7c49cb82020-03-16 23:17:32 +01002660 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2661 int channel, slotrank, lane, err;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002662
Angel Pons7c49cb82020-03-16 23:17:32 +01002663 /*
2664 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2665 * also use a single loop. It would seem that it is a debugging configuration.
2666 */
Angel Pons88521882020-01-05 20:21:20 +01002667 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2668 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002669
2670 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2671 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002672 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002673 if (err)
2674 return err;
2675 }
2676
Angel Pons88521882020-01-05 20:21:20 +01002677 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2678 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002679
2680 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2681 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002682 rising_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002683 if (err)
2684 return err;
2685 }
2686
Angel Pons88521882020-01-05 20:21:20 +01002687 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002688
2689 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2690 ctrl->timings[channel][slotrank].lanes[lane].falling =
Angel Pons7c49cb82020-03-16 23:17:32 +01002691 falling_edges[channel][slotrank][lane];
2692
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002693 ctrl->timings[channel][slotrank].lanes[lane].rising =
Angel Pons7c49cb82020-03-16 23:17:32 +01002694 rising_edges[channel][slotrank][lane];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002695 }
2696
2697 FOR_ALL_POPULATED_CHANNELS
2698 program_timings(ctrl, channel);
2699
2700 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002701 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002702 }
2703 return 0;
2704}
2705
2706static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2707{
Angel Pons88521882020-01-05 20:21:20 +01002708 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002709
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002710 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002711 IOSAV_SUBSEQUENCE(channel, 0,
2712 IOSAV_ACT,
2713 4, MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD), ctrl->tRCD, SSQ_NA,
2714 0, 6, 0, slotrank,
2715 ADDR_UPDATE(0, 0, 1, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002716
2717 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002718 IOSAV_SUBSEQUENCE(channel, 1,
2719 IOSAV_WR,
2720 480, 4, ctrl->tWTR + ctrl->CWL + 8, SSQ_WR,
2721 0, 0, 0, slotrank,
2722 ADDR_UPDATE(0, 1, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002723
2724 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002725 IOSAV_SUBSEQUENCE(channel, 2,
2726 IOSAV_RD,
2727 480, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
2728 0, 0, 0, slotrank,
2729 ADDR_UPDATE(0, 1, 0, 0, 18, 0, 0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002730
2731 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002732 IOSAV_SUBSEQUENCE(channel, 3,
2733 IOSAV_PRE,
2734 1, 4, ctrl->tRP, SSQ_NA,
2735 1024, 6, 0, slotrank,
2736 ADDR_UPDATE_NONE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002737
Angel Pons7c49cb82020-03-16 23:17:32 +01002738 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002739 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002740
Angel Pons88521882020-01-05 20:21:20 +01002741 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002742}
2743
2744int discover_timC_write(ramctr_timing *ctrl)
2745{
Angel Pons7c49cb82020-03-16 23:17:32 +01002746 const u8 rege3c_b24[3] = { 0, 0x0f, 0x2f };
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002747 int i, pat;
2748
2749 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2750 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2751 int channel, slotrank, lane;
2752
2753 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2754 lower[channel][slotrank][lane] = 0;
2755 upper[channel][slotrank][lane] = MAX_TIMC;
2756 }
2757
Angel Pons88521882020-01-05 20:21:20 +01002758 /*
2759 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2760 * FIXME: This must only be done on Ivy Bridge.
2761 */
2762 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002763 printram("discover timC write:\n");
2764
2765 for (i = 0; i < 3; i++)
2766 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002767
2768 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
2769 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel),
2770 ~0x3f000000, rege3c_b24[i] << 24);
2771
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002772 udelay(2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002773
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002774 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2775 FOR_ALL_POPULATED_RANKS {
2776 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01002777 u32 raw_stats[MAX_TIMC + 1];
2778 int stats[MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002779
2780 /* Make sure rn.start < rn.end */
Angel Pons7c49cb82020-03-16 23:17:32 +01002781 stats[MAX_TIMC] = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002782
2783 fill_pattern5(ctrl, channel, pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002784 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
2785
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002786 for (timC = 0; timC < MAX_TIMC; timC++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002787 FOR_ALL_LANES {
2788 ctrl->timings[channel][slotrank]
2789 .lanes[lane].timC = timC;
2790 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002791 program_timings(ctrl, channel);
2792
2793 test_timC_write (ctrl, channel, slotrank);
2794
Angel Pons7c49cb82020-03-16 23:17:32 +01002795 /* FIXME: Another IVB-only register! */
Angel Pons098240eb2020-03-22 12:55:32 +01002796 raw_stats[timC] = MCHBAR32(
2797 IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002798 }
2799 FOR_ALL_LANES {
2800 struct run rn;
Angel Pons7c49cb82020-03-16 23:17:32 +01002801 for (timC = 0; timC < MAX_TIMC; timC++) {
2802 stats[timC] = !!(raw_stats[timC]
2803 & (1 << lane));
2804 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002805
Angel Pons7c49cb82020-03-16 23:17:32 +01002806 rn = get_longest_zero_run(stats, MAX_TIMC + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002807 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002808 printk(BIOS_EMERG,
2809 "timC write discovery failed: "
2810 "%d, %d, %d\n", channel,
2811 slotrank, lane);
2812
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002813 return MAKE_ERR;
2814 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002815 printram("timC: %d, %d, %d: "
2816 "0x%02x-0x%02x-0x%02x, "
2817 "0x%02x-0x%02x\n", channel, slotrank,
2818 i, rn.start, rn.middle, rn.end,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002819 rn.start + ctrl->timC_offset[i],
Angel Pons7c49cb82020-03-16 23:17:32 +01002820 rn.end - ctrl->timC_offset[i]);
2821
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002822 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002823 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002824 lower[channel][slotrank][lane]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002825
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002826 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002827 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002828 upper[channel][slotrank][lane]);
2829
2830 }
2831 }
2832 }
2833 }
2834
2835 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002836 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
Angel Pons88521882020-01-05 20:21:20 +01002837 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002838 udelay(2);
2839 }
2840
Angel Pons88521882020-01-05 20:21:20 +01002841 /*
2842 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2843 * FIXME: This must only be done on Ivy Bridge.
2844 */
2845 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002846
2847 printram("CPB\n");
2848
2849 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002850 printram("timC %d, %d, %d: %x\n", channel, slotrank, lane,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002851 (lower[channel][slotrank][lane] +
2852 upper[channel][slotrank][lane]) / 2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002853
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002854 ctrl->timings[channel][slotrank].lanes[lane].timC =
2855 (lower[channel][slotrank][lane] +
2856 upper[channel][slotrank][lane]) / 2;
2857 }
2858 FOR_ALL_POPULATED_CHANNELS {
2859 program_timings(ctrl, channel);
2860 }
2861 return 0;
2862}
2863
Angel Pons88521882020-01-05 20:21:20 +01002864void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002865{
2866 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002867 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002868
2869 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2870 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002871 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002872 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002873 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01002874 printram("normalize %d, %d, %d: mat %d\n",
2875 channel, slotrank, lane, mat);
2876
Felix Heldef4fe3e2019-12-31 14:15:05 +01002877 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01002878 printram("normalize %d, %d, %d: delta %d\n",
2879 channel, slotrank, lane, delta);
2880
Angel Pons88521882020-01-05 20:21:20 +01002881 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01002882 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002883 }
2884
2885 FOR_ALL_POPULATED_CHANNELS {
2886 program_timings(ctrl, channel);
2887 }
2888}
2889
Angel Pons88521882020-01-05 20:21:20 +01002890void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002891{
2892 int channel, slotrank;
2893
2894 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002895 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002896 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01002897 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002898 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002899 }
2900}
2901
2902int channel_test(ramctr_timing *ctrl)
2903{
2904 int channel, slotrank, lane;
2905
2906 slotrank = 0;
2907 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002908 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01002909 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002910 return MAKE_ERR;
2911 }
2912 FOR_ALL_POPULATED_CHANNELS {
2913 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
2914
Angel Pons88521882020-01-05 20:21:20 +01002915 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002916 }
2917
2918 for (slotrank = 0; slotrank < 4; slotrank++)
2919 FOR_ALL_CHANNELS
2920 if (ctrl->rankmap[channel] & (1 << slotrank)) {
2921 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002922 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
2923 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002924 }
Angel Pons88521882020-01-05 20:21:20 +01002925 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002926
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002927 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002928 IOSAV_SUBSEQUENCE(channel, 0,
2929 IOSAV_ACT,
2930 4, 40, 40, SSQ_NA,
2931 0, 6, 0, slotrank,
2932 ADDR_UPDATE(0, 0, 1, 0, 18, 0, 0, 0));
Felix Held9cf1dd22018-07-31 14:52:40 +02002933
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002934 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002935 IOSAV_SUBSEQUENCE(channel, 1,
2936 IOSAV_WR,
2937 100, 4, 40, SSQ_WR,
2938 0, 0, 0, slotrank,
2939 ADDR_UPDATE(0, 1, 0, 0, 18, 0, 0, 0));
Felix Held9cf1dd22018-07-31 14:52:40 +02002940
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002941 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002942 IOSAV_SUBSEQUENCE(channel, 2,
2943 IOSAV_RD,
2944 100, 4, 40, SSQ_RD,
2945 0, 0, 0, slotrank,
2946 ADDR_UPDATE(0, 1, 0, 0, 18, 0, 0, 0));
Felix Held9cf1dd22018-07-31 14:52:40 +02002947
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002948 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002949 IOSAV_SUBSEQUENCE(channel, 3,
2950 IOSAV_PRE,
2951 1, 3, 40, SSQ_NA,
2952 1024, 6, 0, slotrank,
2953 ADDR_UPDATE(0, 0, 0, 0, 18, 0, 0, 0));
Felix Held9cf1dd22018-07-31 14:52:40 +02002954
Angel Pons7c49cb82020-03-16 23:17:32 +01002955 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002956 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002957
Angel Pons88521882020-01-05 20:21:20 +01002958 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002959 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01002960 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002961 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
2962 channel, slotrank, lane);
2963 return MAKE_ERR;
2964 }
2965 }
2966 return 0;
2967}
2968
Patrick Rudolphdd662872017-10-28 18:20:11 +02002969void channel_scrub(ramctr_timing *ctrl)
2970{
2971 int channel, slotrank, row, rowsize;
2972
2973 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
2974 rowsize = 1 << ctrl->info.dimm[channel][slotrank >> 1].row_bits;
2975 for (row = 0; row < rowsize; row += 16) {
2976
2977 wait_for_iosav(channel);
2978
2979 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002980 IOSAV_SUBSEQUENCE(channel, 0,
2981 IOSAV_ACT,
2982 1, MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD), ctrl->tRCD, SSQ_NA,
2983 row, 6, 0, slotrank,
2984 ADDR_UPDATE(1, 0, 0, 0, 18, 0, 0, 0));
Patrick Rudolphdd662872017-10-28 18:20:11 +02002985
2986 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002987 IOSAV_SUBSEQUENCE(channel, 1,
2988 IOSAV_WR,
2989 129, 4, 40, SSQ_WR,
2990 row, 0, 0, slotrank,
2991 ADDR_UPDATE(0, 1, 0, 0, 18, 0, 0, 0));
Patrick Rudolphdd662872017-10-28 18:20:11 +02002992
2993 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002994 IOSAV_SUBSEQUENCE(channel, 2,
2995 IOSAV_PRE,
2996 1, 3, 40, SSQ_NA,
2997 1024, 6, 0, slotrank,
2998 ADDR_UPDATE(0, 0, 0, 0, 18, 0, 0, 0));
Patrick Rudolphdd662872017-10-28 18:20:11 +02002999
3000 /* execute command queue */
3001 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
3002
3003 wait_for_iosav(channel);
3004 }
3005 }
3006}
3007
Angel Pons88521882020-01-05 20:21:20 +01003008void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003009{
3010 int channel;
3011
Angel Pons7c49cb82020-03-16 23:17:32 +01003012 /* 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 +01003013 static u32 seeds[NUM_CHANNELS][3] = {
3014 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
3015 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
3016 };
3017 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003018 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
Angel Pons7c49cb82020-03-16 23:17:32 +01003019 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
3020 MCHBAR32(SCRAMBLING_SEED_2_HI_ch(channel)) = seeds[channel][1];
3021 MCHBAR32(SCRAMBLING_SEED_2_LO_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003022 }
3023}
3024
Angel Pons89ae6b82020-03-21 13:23:32 +01003025void set_wmm_behavior(const u32 cpu)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003026{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003027 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003028 MCHBAR32(SC_WDBWM) = 0x141d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003029 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01003030 MCHBAR32(SC_WDBWM) = 0x551d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003031 }
3032}
3033
Angel Pons88521882020-01-05 20:21:20 +01003034void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003035{
3036 int channel;
3037
3038 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003039 /* Always drive command bus */
Angel Pons88521882020-01-05 20:21:20 +01003040 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003041 }
3042
3043 udelay(1);
3044
3045 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003046 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003047 }
3048}
3049
Angel Pons7c49cb82020-03-16 23:17:32 +01003050void set_read_write_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003051{
3052 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003053
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003054 FOR_ALL_POPULATED_CHANNELS {
3055 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003056 int min_pi = 10000;
3057 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003058
3059 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003060 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3061 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003062 }
3063
Angel Pons7c49cb82020-03-16 23:17:32 +01003064 b20 = (max_pi - min_pi > 51) ? 0 : ctrl->ref_card_offset[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003065
Angel Pons7c49cb82020-03-16 23:17:32 +01003066 b4_8_12 = (ctrl->pi_coding_threshold < max_pi - min_pi) ? 0x3330 : 0x2220;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003067
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003068 dram_odt_stretch(ctrl, channel);
3069
Angel Pons7c49cb82020-03-16 23:17:32 +01003070 MCHBAR32(TC_RWP_ch(channel)) = 0x0a000000 | (b20 << 20) |
Felix Held2463aa92018-07-29 21:37:55 +02003071 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003072 }
3073}
3074
Angel Pons88521882020-01-05 20:21:20 +01003075void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003076{
3077 int channel;
3078 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003079 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3080 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003081 }
3082}
3083
Angel Pons7c49cb82020-03-16 23:17:32 +01003084/* Encode the watermark latencies in a suitable format for graphics drivers consumption */
3085static int encode_wm(int ns)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003086{
Angel Pons88521882020-01-05 20:21:20 +01003087 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003088}
3089
Angel Pons7c49cb82020-03-16 23:17:32 +01003090/* FIXME: values in this function should be hardware revision-dependent */
Angel Pons88521882020-01-05 20:21:20 +01003091void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003092{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003093 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3094
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003095 int channel;
3096 int t1_cycles = 0, t1_ns = 0, t2_ns;
3097 int t3_ns;
3098 u32 r32;
3099
Angel Pons7c49cb82020-03-16 23:17:32 +01003100 /* FIXME: This register only exists on Ivy Bridge */
3101 MCHBAR32(WMM_READ_CONFIG) = 0x46;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003102
Felix Heldf9b826a2018-07-30 17:56:52 +02003103 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003104 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xffffcfff, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003105
Patrick Rudolph74203de2017-11-20 11:57:01 +01003106 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003107 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003108 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003109 else
Angel Pons7c49cb82020-03-16 23:17:32 +01003110 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003111 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003112
Felix Heldf9b826a2018-07-30 17:56:52 +02003113 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003114 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003115
Angel Pons88521882020-01-05 20:21:20 +01003116 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3117 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003118
3119 FOR_ALL_CHANNELS {
3120 switch (ctrl->rankmap[channel]) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003121 /* Unpopulated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003122 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003123 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003124 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003125 /* Only single-ranked dimms */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003126 case 1:
3127 case 4:
3128 case 5:
Angel Pons7c49cb82020-03-16 23:17:32 +01003129 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x00373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003130 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003131 /* Dual-ranked dimms present */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003132 default:
Angel Pons7c49cb82020-03-16 23:17:32 +01003133 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x009b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003134 break;
3135 }
3136 }
3137
Felix Held50b7ed22019-12-30 20:41:54 +01003138 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
Angel Pons7c49cb82020-03-16 23:17:32 +01003139 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0x00ffffff, 0x00e4d5d0);
Felix Held50b7ed22019-12-30 20:41:54 +01003140 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003141
3142 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003143 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~(3 << 16), 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003144
Angel Pons88521882020-01-05 20:21:20 +01003145 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3146 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3147 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003148
Angel Pons7c49cb82020-03-16 23:17:32 +01003149 /* Find a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003150 FOR_ALL_POPULATED_CHANNELS
3151 break;
3152
Angel Pons88521882020-01-05 20:21:20 +01003153 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3154 r32 = MCHBAR32(PM_DLL_CONFIG);
Angel Pons7c49cb82020-03-16 23:17:32 +01003155 if (r32 & (1 << 17))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003156 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003157 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003158 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
Angel Pons7c49cb82020-03-16 23:17:32 +01003159 if (!(r32 & (1 << 17)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003160 t1_ns += 500;
3161
Angel Pons88521882020-01-05 20:21:20 +01003162 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003163 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003164 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003165 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003166 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003167 t3_ns = 500;
3168 }
Angel Pons7c49cb82020-03-16 23:17:32 +01003169
3170 /* The graphics driver will use these watermark values */
3171 printk(BIOS_DEBUG, "t123: %d, %d, %d\n", t1_ns, t2_ns, t3_ns);
3172 MCHBAR32_AND_OR(SSKPD, 0xC0C0C0C0,
3173 ((encode_wm(t1_ns) + encode_wm(t2_ns)) << 16) | (encode_wm(t1_ns) << 8) |
3174 ((encode_wm(t3_ns) + encode_wm(t2_ns) + encode_wm(t1_ns)) << 24) | 0x0c);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003175}
3176
Angel Pons88521882020-01-05 20:21:20 +01003177void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003178{
3179 int channel, slotrank, lane;
3180
Angel Pons7c49cb82020-03-16 23:17:32 +01003181 FOR_ALL_POPULATED_CHANNELS {
3182 MCHBAR32(TC_RAP_ch(channel)) =
3183 (ctrl->tRRD << 0)
3184 | (ctrl->tRTP << 4)
3185 | (ctrl->tCKE << 8)
3186 | (ctrl->tWTR << 12)
3187 | (ctrl->tFAW << 16)
3188 | (ctrl->tWR << 24)
3189 | (ctrl->cmd_stretch[channel] << 30);
3190 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003191
3192 udelay(1);
3193
3194 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003195 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003196 }
3197
3198 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003199 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003200 }
3201
3202 FOR_ALL_POPULATED_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003203 MCHBAR32_OR(TC_RWP_ch(channel), 0x08000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003204
3205 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003206 udelay(1);
3207 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x00200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003208 }
3209
3210 printram("CPE\n");
3211
Angel Pons88521882020-01-05 20:21:20 +01003212 MCHBAR32(GDCRTRAININGMOD) = 0;
3213 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003214
3215 printram("CP5b\n");
3216
3217 FOR_ALL_POPULATED_CHANNELS {
3218 program_timings(ctrl, channel);
3219 }
3220
3221 u32 reg, addr;
3222
Angel Pons7c49cb82020-03-16 23:17:32 +01003223 /* Poll for RCOMP */
3224 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
3225 ;
3226
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003227 do {
Angel Pons88521882020-01-05 20:21:20 +01003228 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003229 } while ((reg & 0x14) == 0);
3230
Angel Pons7c49cb82020-03-16 23:17:32 +01003231 /* Set state of memory controller */
Angel Pons88521882020-01-05 20:21:20 +01003232 MCHBAR32(MC_INIT_STATE_G) = 0x116;
Angel Pons7c49cb82020-03-16 23:17:32 +01003233 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003234
Angel Pons7c49cb82020-03-16 23:17:32 +01003235 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003236 udelay(500);
3237
3238 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003239 /* Set valid rank CKE */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003240 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01003241 reg = (reg & ~0x0f) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003242 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003243 MCHBAR32(addr) = reg;
3244
Angel Pons7c49cb82020-03-16 23:17:32 +01003245 /* Wait 10ns for ranks to settle */
3246 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003247
3248 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3249 MCHBAR32(addr) = reg;
3250
Angel Pons7c49cb82020-03-16 23:17:32 +01003251 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003252 write_reset(ctrl);
3253 }
3254
Angel Pons7c49cb82020-03-16 23:17:32 +01003255 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003256 dram_mrscommands(ctrl);
3257
3258 printram("CP5c\n");
3259
Angel Pons88521882020-01-05 20:21:20 +01003260 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003261
3262 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003263 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003264 udelay(2);
3265 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003266}