blob: a8480a766134ea500786151ff8787b23a8be3eb9 [file] [log] [blame]
Angel Pons6e5aabd2020-03-23 23:44:42 +01001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01004#include <commonlib/helpers.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01005#include <console/console.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01006#include <string.h>
Subrata Banik53b08c32018-12-10 14:11:35 +05307#include <arch/cpu.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +02008#include <device/mmio.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +02009#include <device/pci_ops.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010010#include <northbridge/intel/sandybridge/chip.h>
11#include <device/pci_def.h>
12#include <delay.h>
Elyes HAOUAS1d3b3c32019-05-04 08:12:42 +020013
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010014#include "raminit_native.h"
15#include "raminit_common.h"
Angel Pons7f6586f2020-03-21 12:45:12 +010016#include "raminit_tables.h"
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010017#include "sandybridge.h"
18
Angel Pons7c49cb82020-03-16 23:17:32 +010019/* FIXME: no ECC support */
20/* FIXME: no support for 3-channel chipsets */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010021
Angel Pons88521882020-01-05 20:21:20 +010022/* length: [1..4] */
23#define IOSAV_RUN_ONCE(length) ((((length) - 1) << 18) | 1)
Felix Held9cf1dd22018-07-31 14:52:40 +020024
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010025static void sfence(void)
26{
27 asm volatile ("sfence");
28}
29
Angel Pons7c49cb82020-03-16 23:17:32 +010030/* Toggle IO reset bit */
31static void toggle_io_reset(void)
32{
Angel Pons88521882020-01-05 20:21:20 +010033 u32 r32 = MCHBAR32(MC_INIT_STATE_G);
Angel Pons7c49cb82020-03-16 23:17:32 +010034 MCHBAR32(MC_INIT_STATE_G) = r32 | 0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010035 udelay(1);
Angel Pons88521882020-01-05 20:21:20 +010036 MCHBAR32(MC_INIT_STATE_G) = r32 & ~0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010037 udelay(1);
38}
39
40static u32 get_XOVER_CLK(u8 rankmap)
41{
42 return rankmap << 24;
43}
44
45static u32 get_XOVER_CMD(u8 rankmap)
46{
47 u32 reg;
48
Angel Pons7c49cb82020-03-16 23:17:32 +010049 /* Enable xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010050 reg = 0x4000;
51
Angel Pons7c49cb82020-03-16 23:17:32 +010052 /* Enable xover ctl */
53 if (rankmap & 0x03)
54 reg |= (1 << 17);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010055
Angel Pons7c49cb82020-03-16 23:17:32 +010056 if (rankmap & 0x0c)
57 reg |= (1 << 26);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010058
59 return reg;
60}
61
Angel Pons7c49cb82020-03-16 23:17:32 +010062/* CAS write latency. To be programmed in MR2. See DDR3 SPEC for MR2 documentation. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010063u8 get_CWL(u32 tCK)
64{
Angel Pons7c49cb82020-03-16 23:17:32 +010065 /* Get CWL based on tCK using the following rule */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010066 switch (tCK) {
67 case TCK_1333MHZ:
68 return 12;
Angel Pons7c49cb82020-03-16 23:17:32 +010069
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010070 case TCK_1200MHZ:
71 case TCK_1100MHZ:
72 return 11;
Angel Pons7c49cb82020-03-16 23:17:32 +010073
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010074 case TCK_1066MHZ:
75 case TCK_1000MHZ:
76 return 10;
Angel Pons7c49cb82020-03-16 23:17:32 +010077
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010078 case TCK_933MHZ:
79 case TCK_900MHZ:
80 return 9;
Angel Pons7c49cb82020-03-16 23:17:32 +010081
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010082 case TCK_800MHZ:
83 case TCK_700MHZ:
84 return 8;
Angel Pons7c49cb82020-03-16 23:17:32 +010085
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010086 case TCK_666MHZ:
87 return 7;
Angel Pons7c49cb82020-03-16 23:17:32 +010088
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010089 case TCK_533MHZ:
90 return 6;
Angel Pons7c49cb82020-03-16 23:17:32 +010091
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010092 default:
93 return 5;
94 }
95}
96
97void dram_find_common_params(ramctr_timing *ctrl)
98{
99 size_t valid_dimms;
100 int channel, slot;
101 dimm_info *dimms = &ctrl->info;
102
103 ctrl->cas_supported = (1 << (MAX_CAS - MIN_CAS + 1)) - 1;
104 valid_dimms = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100105
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100106 FOR_ALL_CHANNELS for (slot = 0; slot < 2; slot++) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100107
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100108 const dimm_attr *dimm = &dimms->dimm[channel][slot];
109 if (dimm->dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3)
110 continue;
Angel Pons7c49cb82020-03-16 23:17:32 +0100111
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100112 valid_dimms++;
113
114 /* Find all possible CAS combinations */
115 ctrl->cas_supported &= dimm->cas_supported;
116
117 /* Find the smallest common latencies supported by all DIMMs */
Angel Pons7c49cb82020-03-16 23:17:32 +0100118 ctrl->tCK = MAX(ctrl->tCK, dimm->tCK);
119 ctrl->tAA = MAX(ctrl->tAA, dimm->tAA);
120 ctrl->tWR = MAX(ctrl->tWR, dimm->tWR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100121 ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD);
122 ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD);
Angel Pons7c49cb82020-03-16 23:17:32 +0100123 ctrl->tRP = MAX(ctrl->tRP, dimm->tRP);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100124 ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS);
125 ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC);
126 ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR);
127 ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP);
128 ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW);
Dan Elkoubydabebc32018-04-13 18:47:10 +0300129 ctrl->tCWL = MAX(ctrl->tCWL, dimm->tCWL);
130 ctrl->tCMD = MAX(ctrl->tCMD, dimm->tCMD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100131 }
132
133 if (!ctrl->cas_supported)
Angel Pons7c49cb82020-03-16 23:17:32 +0100134 die("Unsupported DIMM combination. DIMMS do not support common CAS latency");
135
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100136 if (!valid_dimms)
137 die("No valid DIMMs found");
138}
139
Angel Pons88521882020-01-05 20:21:20 +0100140void dram_xover(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100141{
142 u32 reg;
143 int channel;
144
145 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100146 /* Enable xover clk */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100147 reg = get_XOVER_CLK(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100148 printram("XOVER CLK [%x] = %x\n", GDCRCKPICODE_ch(channel), reg);
149 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100150
Angel Pons7c49cb82020-03-16 23:17:32 +0100151 /* Enable xover ctl & xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100152 reg = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100153 printram("XOVER CMD [%x] = %x\n", GDCRCMDPICODING_ch(channel), reg);
154 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100155 }
156}
157
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100158static void dram_odt_stretch(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100159{
Angel Pons89ae6b82020-03-21 13:23:32 +0100160 u32 addr, stretch;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100161
162 stretch = ctrl->ref_card_offset[channel];
Angel Pons7c49cb82020-03-16 23:17:32 +0100163 /*
164 * ODT stretch:
165 * Delay ODT signal by stretch value. Useful for multi DIMM setups on the same channel.
166 */
Angel Pons89ae6b82020-03-21 13:23:32 +0100167 if (IS_SANDY_CPU(ctrl->cpu) && IS_SANDY_CPU_C(ctrl->cpu)) {
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100168 if (stretch == 2)
169 stretch = 3;
Angel Pons7c49cb82020-03-16 23:17:32 +0100170
Angel Pons88521882020-01-05 20:21:20 +0100171 addr = SCHED_SECOND_CBIT_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100172 MCHBAR32_AND_OR(addr, 0xffffc3ff, (stretch << 12) | (stretch << 10));
173 printk(RAM_DEBUG, "OTHP Workaround [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100174 } else {
Angel Pons88521882020-01-05 20:21:20 +0100175 addr = TC_OTHP_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100176 MCHBAR32_AND_OR(addr, 0xfff0ffff, (stretch << 16) | (stretch << 18));
Iru Cai89af71c2018-08-16 16:46:27 +0800177 printk(RAM_DEBUG, "OTHP [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100178 }
179}
180
181void dram_timing_regs(ramctr_timing *ctrl)
182{
183 u32 reg, addr, val32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100184 int channel;
185
186 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100187 /* BIN parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100188 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100189 reg |= (ctrl->tRCD << 0);
190 reg |= (ctrl->tRP << 4);
191 reg |= (ctrl->CAS << 8);
192 reg |= (ctrl->CWL << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100193 reg |= (ctrl->tRAS << 16);
Angel Pons88521882020-01-05 20:21:20 +0100194 printram("DBP [%x] = %x\n", TC_DBP_ch(channel), reg);
195 MCHBAR32(TC_DBP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100196
Angel Pons7c49cb82020-03-16 23:17:32 +0100197 /* Regular access parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100198 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100199 reg |= (ctrl->tRRD << 0);
200 reg |= (ctrl->tRTP << 4);
201 reg |= (ctrl->tCKE << 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100202 reg |= (ctrl->tWTR << 12);
203 reg |= (ctrl->tFAW << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +0100204 reg |= (ctrl->tWR << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100205 reg |= (3 << 30);
Angel Pons88521882020-01-05 20:21:20 +0100206 printram("RAP [%x] = %x\n", TC_RAP_ch(channel), reg);
207 MCHBAR32(TC_RAP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100208
Angel Pons7c49cb82020-03-16 23:17:32 +0100209 /* Other parameters */
Angel Pons88521882020-01-05 20:21:20 +0100210 addr = TC_OTHP_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100211 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100212 reg |= (ctrl->tXPDLL << 0);
213 reg |= (ctrl->tXP << 5);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100214 reg |= (ctrl->tAONPD << 8);
215 reg |= 0xa0000;
216 printram("OTHP [%x] = %x\n", addr, reg);
217 MCHBAR32(addr) = reg;
218
Angel Pons7c49cb82020-03-16 23:17:32 +0100219 /* FIXME: This register might as well not exist */
Angel Pons1aba2a32020-01-05 22:31:41 +0100220 MCHBAR32(0x4014 + channel * 0x400) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100221
Felix Held9fe248f2018-07-31 20:59:45 +0200222 MCHBAR32_OR(addr, 0x00020000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100223
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100224 dram_odt_stretch(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100225
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100226 /*
Angel Pons7c49cb82020-03-16 23:17:32 +0100227 * TC-Refresh timing parameters:
228 * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow
229 * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024.
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100230 */
231 val32 = MIN((ctrl->tREFI * 89) / 10, (70000 << 8) / ctrl->tCK);
232
Angel Pons7c49cb82020-03-16 23:17:32 +0100233 reg = ((ctrl->tREFI & 0xffff) << 0) |
234 ((ctrl->tRFC & 0x01ff) << 16) | (((val32 / 1024) & 0x7f) << 25);
235
Angel Pons88521882020-01-05 20:21:20 +0100236 printram("REFI [%x] = %x\n", TC_RFTP_ch(channel), reg);
237 MCHBAR32(TC_RFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100238
Angel Pons88521882020-01-05 20:21:20 +0100239 MCHBAR32_OR(TC_RFP_ch(channel), 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100240
Angel Pons7c49cb82020-03-16 23:17:32 +0100241 /* Self-refresh timing parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100242 reg = 0;
243 val32 = tDLLK;
Angel Pons7c49cb82020-03-16 23:17:32 +0100244 reg = (reg & ~0x00000fff) | (val32 << 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100245 val32 = ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100246 reg = (reg & ~0x0000f000) | (val32 << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100247 val32 = tDLLK - ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100248 reg = (reg & ~0x03ff0000) | (val32 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100249 val32 = ctrl->tMOD - 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100250 reg = (reg & ~0xf0000000) | (val32 << 28);
251 printram("SRFTP [%x] = %x\n", TC_SRFTP_ch(channel), reg);
Angel Pons88521882020-01-05 20:21:20 +0100252 MCHBAR32(TC_SRFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100253 }
254}
255
256void dram_dimm_mapping(ramctr_timing *ctrl)
257{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100258 int channel;
259 dimm_info *info = &ctrl->info;
260
261 FOR_ALL_CHANNELS {
Nico Huberac4f2162017-10-01 18:14:43 +0200262 dimm_attr *dimmA, *dimmB;
263 u32 reg = 0;
264
Angel Pons7c49cb82020-03-16 23:17:32 +0100265 if (info->dimm[channel][0].size_mb >= info->dimm[channel][1].size_mb) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100266 dimmA = &info->dimm[channel][0];
267 dimmB = &info->dimm[channel][1];
Angel Pons7c49cb82020-03-16 23:17:32 +0100268 reg |= (0 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100269 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100270 dimmA = &info->dimm[channel][1];
271 dimmB = &info->dimm[channel][0];
Angel Pons7c49cb82020-03-16 23:17:32 +0100272 reg |= (1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100273 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100274
Nico Huberac4f2162017-10-01 18:14:43 +0200275 if (dimmA && (dimmA->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100276 reg |= (dimmA->size_mb / 256) << 0;
277 reg |= (dimmA->ranks - 1) << 17;
Nico Huberac4f2162017-10-01 18:14:43 +0200278 reg |= (dimmA->width / 8 - 1) << 19;
279 }
280
281 if (dimmB && (dimmB->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100282 reg |= (dimmB->size_mb / 256) << 8;
283 reg |= (dimmB->ranks - 1) << 18;
Nico Huberac4f2162017-10-01 18:14:43 +0200284 reg |= (dimmB->width / 8 - 1) << 20;
285 }
286
Angel Pons7c49cb82020-03-16 23:17:32 +0100287 reg |= 1 << 21; /* Rank interleave */
288 reg |= 1 << 22; /* Enhanced interleave */
Nico Huberac4f2162017-10-01 18:14:43 +0200289
Angel Pons7c49cb82020-03-16 23:17:32 +0100290 if ((dimmA && (dimmA->ranks > 0)) || (dimmB && (dimmB->ranks > 0))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100291 ctrl->mad_dimm[channel] = reg;
292 } else {
293 ctrl->mad_dimm[channel] = 0;
294 }
295 }
296}
297
Angel Pons88521882020-01-05 20:21:20 +0100298void dram_dimm_set_mapping(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100299{
300 int channel;
301 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100302 MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100303 }
304}
305
Angel Pons88521882020-01-05 20:21:20 +0100306void dram_zones(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100307{
308 u32 reg, ch0size, ch1size;
309 u8 val;
310 reg = 0;
311 val = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100312
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100313 if (training) {
314 ch0size = ctrl->channel_size_mb[0] ? 256 : 0;
315 ch1size = ctrl->channel_size_mb[1] ? 256 : 0;
316 } else {
317 ch0size = ctrl->channel_size_mb[0];
318 ch1size = ctrl->channel_size_mb[1];
319 }
320
321 if (ch0size >= ch1size) {
Angel Pons88521882020-01-05 20:21:20 +0100322 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100323 val = ch1size / 256;
324 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100325 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100326 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100327 MCHBAR32(MAD_CHNL) = 0x24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100328
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100329 } else {
Angel Pons88521882020-01-05 20:21:20 +0100330 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100331 val = ch0size / 256;
332 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100333 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100334 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100335 MCHBAR32(MAD_CHNL) = 0x21;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100336 }
337}
338
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100339#define DEFAULT_TCK TCK_800MHZ
340
341unsigned int get_mem_min_tck(void)
342{
343 u32 reg32;
344 u8 rev;
345 const struct device *dev;
346 const struct northbridge_intel_sandybridge_config *cfg = NULL;
347
Angel Ponsb31d1d72020-01-10 01:35:09 +0100348 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100349 if (dev)
350 cfg = dev->chip_info;
351
352 /* If this is zero, it just means devicetree.cb didn't set it */
353 if (!cfg || cfg->max_mem_clock_mhz == 0) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100354
Julius Wernercd49cce2019-03-05 16:53:33 -0800355 if (CONFIG(NATIVE_RAMINIT_IGNORE_MAX_MEM_FUSES))
Patrick Rudolphb794a692017-08-08 13:13:51 +0200356 return TCK_1333MHZ;
357
Angel Ponsb31d1d72020-01-10 01:35:09 +0100358 rev = pci_read_config8(HOST_BRIDGE, PCI_DEVICE_ID);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100359
360 if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100361 /* Read Capabilities A Register DMFC bits */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100362 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100363 reg32 &= 0x7;
364
365 switch (reg32) {
366 case 7: return TCK_533MHZ;
367 case 6: return TCK_666MHZ;
368 case 5: return TCK_800MHZ;
Angel Pons7c49cb82020-03-16 23:17:32 +0100369 /* Reserved */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100370 default:
371 break;
372 }
373 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +0100374 /* Read Capabilities B Register DMFC bits */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100375 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_B);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100376 reg32 = (reg32 >> 4) & 0x7;
377
378 switch (reg32) {
379 case 7: return TCK_533MHZ;
380 case 6: return TCK_666MHZ;
381 case 5: return TCK_800MHZ;
382 case 4: return TCK_933MHZ;
383 case 3: return TCK_1066MHZ;
384 case 2: return TCK_1200MHZ;
385 case 1: return TCK_1333MHZ;
Angel Pons7c49cb82020-03-16 23:17:32 +0100386 /* Reserved */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100387 default:
388 break;
389 }
390 }
391 return DEFAULT_TCK;
392 } else {
393 if (cfg->max_mem_clock_mhz >= 1066)
394 return TCK_1066MHZ;
395 else if (cfg->max_mem_clock_mhz >= 933)
396 return TCK_933MHZ;
397 else if (cfg->max_mem_clock_mhz >= 800)
398 return TCK_800MHZ;
399 else if (cfg->max_mem_clock_mhz >= 666)
400 return TCK_666MHZ;
401 else if (cfg->max_mem_clock_mhz >= 533)
402 return TCK_533MHZ;
403 else
404 return TCK_400MHZ;
405 }
406}
407
408#define DEFAULT_PCI_MMIO_SIZE 2048
409
410static unsigned int get_mmio_size(void)
411{
412 const struct device *dev;
413 const struct northbridge_intel_sandybridge_config *cfg = NULL;
414
Angel Ponsb31d1d72020-01-10 01:35:09 +0100415 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100416 if (dev)
417 cfg = dev->chip_info;
418
419 /* If this is zero, it just means devicetree.cb didn't set it */
420 if (!cfg || cfg->pci_mmio_size == 0)
421 return DEFAULT_PCI_MMIO_SIZE;
422 else
423 return cfg->pci_mmio_size;
424}
425
Angel Pons88521882020-01-05 20:21:20 +0100426void dram_memorymap(ramctr_timing *ctrl, int me_uma_size)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100427{
Angel Pons7c49cb82020-03-16 23:17:32 +0100428 u32 reg, val, reclaim, tom, gfxstolen, gttsize;
429 size_t tsegbase, toludbase, remapbase, gfxstolenbase, mmiosize, gttbase;
430 size_t tsegsize, touudbase, remaplimit, mestolenbase, tsegbasedelta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100431 uint16_t ggc;
432
433 mmiosize = get_mmio_size();
434
Felix Held87ddea22020-01-26 04:55:27 +0100435 ggc = pci_read_config16(HOST_BRIDGE, GGC);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100436 if (!(ggc & 2)) {
437 gfxstolen = ((ggc >> 3) & 0x1f) * 32;
Angel Pons7c49cb82020-03-16 23:17:32 +0100438 gttsize = ((ggc >> 8) & 0x3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100439 } else {
440 gfxstolen = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100441 gttsize = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100442 }
443
444 tsegsize = CONFIG_SMM_TSEG_SIZE >> 20;
445
446 tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1];
447
448 mestolenbase = tom - me_uma_size;
449
Angel Pons7c49cb82020-03-16 23:17:32 +0100450 toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize, tom - me_uma_size);
451
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100452 gfxstolenbase = toludbase - gfxstolen;
453 gttbase = gfxstolenbase - gttsize;
454
455 tsegbase = gttbase - tsegsize;
456
Angel Pons7c49cb82020-03-16 23:17:32 +0100457 /* Round tsegbase down to nearest address aligned to tsegsize */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100458 tsegbasedelta = tsegbase & (tsegsize - 1);
459 tsegbase &= ~(tsegsize - 1);
460
461 gttbase -= tsegbasedelta;
462 gfxstolenbase -= tsegbasedelta;
463 toludbase -= tsegbasedelta;
464
Angel Pons7c49cb82020-03-16 23:17:32 +0100465 /* Test if it is possible to reclaim a hole in the RAM addressing */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100466 if (tom - me_uma_size > toludbase) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100467 /* Reclaim is possible */
468 reclaim = 1;
469 remapbase = MAX(4096, tom - me_uma_size);
470 remaplimit = remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1;
471 touudbase = remaplimit + 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100472 } else {
473 // Reclaim not possible
Angel Pons7c49cb82020-03-16 23:17:32 +0100474 reclaim = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100475 touudbase = tom - me_uma_size;
476 }
477
Angel Pons7c49cb82020-03-16 23:17:32 +0100478 /* Update memory map in PCIe configuration space */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100479 printk(BIOS_DEBUG, "Update PCI-E configuration space:\n");
480
Angel Pons7c49cb82020-03-16 23:17:32 +0100481 /* TOM (top of memory) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100482 reg = pci_read_config32(HOST_BRIDGE, TOM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100483 val = tom & 0xfff;
484 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100485 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100486 pci_write_config32(HOST_BRIDGE, TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100487
Angel Ponsb31d1d72020-01-10 01:35:09 +0100488 reg = pci_read_config32(HOST_BRIDGE, TOM + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100489 val = tom & 0xfffff000;
490 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100491 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100492 pci_write_config32(HOST_BRIDGE, TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100493
Angel Pons7c49cb82020-03-16 23:17:32 +0100494 /* TOLUD (Top Of Low Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100495 reg = pci_read_config32(HOST_BRIDGE, TOLUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100496 val = toludbase & 0xfff;
497 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100498 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOLUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100499 pci_write_config32(HOST_BRIDGE, TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100500
Angel Pons7c49cb82020-03-16 23:17:32 +0100501 /* TOUUD LSB (Top Of Upper Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100502 reg = pci_read_config32(HOST_BRIDGE, TOUUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100503 val = touudbase & 0xfff;
504 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100505 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100506 pci_write_config32(HOST_BRIDGE, TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100507
Angel Pons7c49cb82020-03-16 23:17:32 +0100508 /* TOUUD MSB */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100509 reg = pci_read_config32(HOST_BRIDGE, TOUUD + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100510 val = touudbase & 0xfffff000;
511 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100512 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100513 pci_write_config32(HOST_BRIDGE, TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100514
515 if (reclaim) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100516 /* REMAP BASE */
517 pci_write_config32(HOST_BRIDGE, REMAPBASE, remapbase << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100518 pci_write_config32(HOST_BRIDGE, REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100519
Angel Pons7c49cb82020-03-16 23:17:32 +0100520 /* REMAP LIMIT */
521 pci_write_config32(HOST_BRIDGE, REMAPLIMIT, remaplimit << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100522 pci_write_config32(HOST_BRIDGE, REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100523 }
Angel Pons7c49cb82020-03-16 23:17:32 +0100524 /* TSEG */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100525 reg = pci_read_config32(HOST_BRIDGE, TSEGMB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100526 val = tsegbase & 0xfff;
527 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100528 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TSEGMB, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100529 pci_write_config32(HOST_BRIDGE, TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100530
Angel Pons7c49cb82020-03-16 23:17:32 +0100531 /* GFX stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100532 reg = pci_read_config32(HOST_BRIDGE, BDSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100533 val = gfxstolenbase & 0xfff;
534 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100535 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BDSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100536 pci_write_config32(HOST_BRIDGE, BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100537
Angel Pons7c49cb82020-03-16 23:17:32 +0100538 /* GTT stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100539 reg = pci_read_config32(HOST_BRIDGE, BGSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100540 val = gttbase & 0xfff;
541 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100542 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BGSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100543 pci_write_config32(HOST_BRIDGE, BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100544
545 if (me_uma_size) {
Angel Ponsb31d1d72020-01-10 01:35:09 +0100546 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100547 val = (0x80000 - me_uma_size) & 0xfffff000;
548 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100549 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100550 pci_write_config32(HOST_BRIDGE, MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100551
Angel Pons7c49cb82020-03-16 23:17:32 +0100552 /* ME base */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100553 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100554 val = mestolenbase & 0xfff;
555 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held651f99f2019-12-30 16:28:48 +0100556 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100557 pci_write_config32(HOST_BRIDGE, MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100558
Angel Ponsb31d1d72020-01-10 01:35:09 +0100559 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100560 val = mestolenbase & 0xfffff000;
561 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100562 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100563 pci_write_config32(HOST_BRIDGE, MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100564
Angel Pons7c49cb82020-03-16 23:17:32 +0100565 /* ME mask */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100566 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100567 val = (0x80000 - me_uma_size) & 0xfff;
568 reg = (reg & ~0xfff00000) | (val << 20);
Angel Pons7c49cb82020-03-16 23:17:32 +0100569 reg = reg | ME_STLEN_EN; /* Set ME memory enable */
570 reg = reg | MELCK; /* Set lock bit on ME mem */
Felix Held651f99f2019-12-30 16:28:48 +0100571 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100572 pci_write_config32(HOST_BRIDGE, MESEG_MASK, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100573 }
574}
575
Angel Pons88521882020-01-05 20:21:20 +0100576static void wait_for_iosav(int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100577{
578 while (1) {
Angel Pons88521882020-01-05 20:21:20 +0100579 if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100580 return;
581 }
582}
583
Angel Pons88521882020-01-05 20:21:20 +0100584static void write_reset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100585{
586 int channel, slotrank;
587
Angel Pons7c49cb82020-03-16 23:17:32 +0100588 /* Choose a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100589 channel = (ctrl->rankmap[0]) ? 0 : 1;
590
Angel Pons88521882020-01-05 20:21:20 +0100591 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100592
Angel Pons7c49cb82020-03-16 23:17:32 +0100593 /* Choose a populated rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100594 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
595
596 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100597 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
598 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x80c01;
Angel Pons88521882020-01-05 20:21:20 +0100599 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100600 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100601
Angel Pons7c49cb82020-03-16 23:17:32 +0100602 /*
603 * Execute command queue - why is bit 22 set here?!
604 *
605 * This is actually using the IOSAV state machine as a timer, so refresh is allowed.
606 */
Angel Pons88521882020-01-05 20:21:20 +0100607 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = (1 << 22) | IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +0200608
Angel Pons88521882020-01-05 20:21:20 +0100609 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100610}
611
Angel Pons88521882020-01-05 20:21:20 +0100612void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100613{
Felix Held9fe248f2018-07-31 20:59:45 +0200614 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100615 int channel;
616
Angel Pons7c49cb82020-03-16 23:17:32 +0100617 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
618 ;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100619 do {
Angel Pons88521882020-01-05 20:21:20 +0100620 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100621 } while ((reg & 0x14) == 0);
622
Angel Pons7c49cb82020-03-16 23:17:32 +0100623 /* Set state of memory controller */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100624 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100625 MCHBAR32(MC_INIT_STATE_G) = reg;
626 MCHBAR32(MC_INIT_STATE) = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100627 reg |= 2; /* DDR reset */
Angel Pons88521882020-01-05 20:21:20 +0100628 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100629
Angel Pons7c49cb82020-03-16 23:17:32 +0100630 /* Assert DIMM reset signal */
631 MCHBAR32_AND(MC_INIT_STATE_G, ~2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100632
Angel Pons7c49cb82020-03-16 23:17:32 +0100633 /* Wait 200us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100634 udelay(200);
635
Angel Pons7c49cb82020-03-16 23:17:32 +0100636 /* Deassert DIMM reset signal */
Angel Pons88521882020-01-05 20:21:20 +0100637 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100638
Angel Pons7c49cb82020-03-16 23:17:32 +0100639 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100640 udelay(500);
641
Angel Pons7c49cb82020-03-16 23:17:32 +0100642 /* Enable DCLK */
Angel Pons88521882020-01-05 20:21:20 +0100643 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100644
Angel Pons7c49cb82020-03-16 23:17:32 +0100645 /* XXX Wait 20ns */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100646 udelay(1);
647
648 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100649 /* Set valid rank CKE */
Felix Held9fe248f2018-07-31 20:59:45 +0200650 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100651 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100652
Angel Pons7c49cb82020-03-16 23:17:32 +0100653 /* Wait 10ns for ranks to settle */
654 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100655
656 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100657 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100658
Angel Pons7c49cb82020-03-16 23:17:32 +0100659 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100660 write_reset(ctrl);
661 }
662}
663
664static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
665{
Angel Pons7c49cb82020-03-16 23:17:32 +0100666 /* Get ODT based on rankmap */
667 int dimms_per_ch = (ctrl->rankmap[channel] & 1) + ((ctrl->rankmap[channel] >> 2) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100668
669 if (dimms_per_ch == 1) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100670 return (const odtmap){60, 60};
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100671 } else {
672 return (const odtmap){120, 30};
673 }
674}
675
Angel Pons7c49cb82020-03-16 23:17:32 +0100676static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank, int reg, u32 val)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100677{
Angel Pons88521882020-01-05 20:21:20 +0100678 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100679
680 if (ctrl->rank_mirror[channel][slotrank]) {
681 /* DDR3 Rank1 Address mirror
Angel Pons7c49cb82020-03-16 23:17:32 +0100682 swap the following pins:
683 A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100684 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
Angel Pons7c49cb82020-03-16 23:17:32 +0100685 val = (val & ~0x1f8) | ((val >> 1) & 0xa8) | ((val & 0xa8) << 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100686 }
687
688 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100689 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f000;
690 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100691 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200692 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100693 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100694
695 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100696 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f000;
697 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100698 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200699 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100700 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100701
702 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100703 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x0f000;
704 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +0100705 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200706 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100707 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200708
Angel Pons7c49cb82020-03-16 23:17:32 +0100709 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100710 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100711}
712
Angel Pons88521882020-01-05 20:21:20 +0100713static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100714{
715 u16 mr0reg, mch_cas, mch_wr;
716 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 +0100717 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100718
719 /* DLL Reset - self clearing - set after CLK frequency has been changed */
720 mr0reg = 0x100;
721
Angel Pons7c49cb82020-03-16 23:17:32 +0100722 /* Convert CAS to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100723 if (ctrl->CAS < 12) {
724 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
725 } else {
726 mch_cas = (u16) (ctrl->CAS - 12);
727 mch_cas = ((mch_cas << 1) | 0x1);
728 }
729
Angel Pons7c49cb82020-03-16 23:17:32 +0100730 /* Convert tWR to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100731 mch_wr = mch_wr_t[ctrl->tWR - 5];
732
Angel Pons7c49cb82020-03-16 23:17:32 +0100733 mr0reg = (mr0reg & ~0x0004) | ((mch_cas & 0x1) << 2);
734 mr0reg = (mr0reg & ~0x0070) | ((mch_cas & 0xe) << 3);
735 mr0reg = (mr0reg & ~0x0e00) | (mch_wr << 9);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100736
Angel Pons7c49cb82020-03-16 23:17:32 +0100737 /* Precharge PD - Fast (desktop) 1 or slow (mobile) 0 - mostly power-saving feature */
738 mr0reg = (mr0reg & ~(1 << 12)) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100739 return mr0reg;
740}
741
742static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
743{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200744 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100745}
746
747static u32 encode_odt(u32 odt)
748{
749 switch (odt) {
750 case 30:
751 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
752 case 60:
753 return (1 << 2); // RZQ/4
754 case 120:
755 return (1 << 6); // RZQ/2
756 default:
757 case 0:
758 return 0;
759 }
760}
761
762static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
763{
764 odtmap odt;
765 u32 mr1reg;
766
767 odt = get_ODT(ctrl, rank, channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100768 mr1reg = 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100769
770 mr1reg |= encode_odt(odt.rttnom);
771
772 return mr1reg;
773}
774
775static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
776{
777 u16 mr1reg;
778
779 mr1reg = make_mr1(ctrl, rank, channel);
780
781 write_mrreg(ctrl, channel, rank, 1, mr1reg);
782}
783
784static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
785{
786 u16 pasr, cwl, mr2reg;
787 odtmap odt;
788 int srt;
789
790 pasr = 0;
791 cwl = ctrl->CWL - 5;
792 odt = get_ODT(ctrl, rank, channel);
793
794 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
795
796 mr2reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100797 mr2reg = (mr2reg & ~0x07) | pasr;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100798 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
799 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
800 mr2reg = (mr2reg & ~0x80) | (srt << 7);
801 mr2reg |= (odt.rttwr / 60) << 9;
802
803 write_mrreg(ctrl, channel, rank, 2, mr2reg);
804}
805
806static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
807{
808 write_mrreg(ctrl, channel, rank, 3, 0);
809}
810
Angel Pons88521882020-01-05 20:21:20 +0100811void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100812{
813 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100814 int channel;
815
816 FOR_ALL_POPULATED_CHANNELS {
817 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100818 /* MR2 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100819 dram_mr2(ctrl, slotrank, channel);
820
Angel Pons7c49cb82020-03-16 23:17:32 +0100821 /* MR3 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100822 dram_mr3(ctrl, slotrank, channel);
823
Angel Pons7c49cb82020-03-16 23:17:32 +0100824 /* MR1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100825 dram_mr1(ctrl, slotrank, channel);
826
Angel Pons7c49cb82020-03-16 23:17:32 +0100827 /* MR0 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100828 dram_mr0(ctrl, slotrank, channel);
829 }
830 }
831
832 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +0100833 MCHBAR32(IOSAV_n_SP_CMD_CTRL(0)) = 0x7;
834 MCHBAR32(IOSAV_n_SUBSEQ_CTRL(0)) = 0xf1001;
Angel Pons88521882020-01-05 20:21:20 +0100835 MCHBAR32(IOSAV_n_SP_CMD_ADDR(0)) = 0x60002;
Angel Pons7c49cb82020-03-16 23:17:32 +0100836 MCHBAR32(IOSAV_n_ADDR_UPDATE(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100837
838 /* DRAM command ZQCL */
Angel Pons7c49cb82020-03-16 23:17:32 +0100839 MCHBAR32(IOSAV_n_SP_CMD_CTRL(1)) = 0x1f003;
840 MCHBAR32(IOSAV_n_SUBSEQ_CTRL(1)) = 0x1901001;
Angel Pons88521882020-01-05 20:21:20 +0100841 MCHBAR32(IOSAV_n_SP_CMD_ADDR(1)) = 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +0100842 MCHBAR32(IOSAV_n_ADDR_UPDATE(1)) = 0x288;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100843
Angel Pons7c49cb82020-03-16 23:17:32 +0100844 /* Execute command queue on all channels. Do it four times. */
845 MCHBAR32(IOSAV_SEQ_CTL) = (1 << 18) | 4;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100846
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100847 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100848 /* Wait for ref drained */
Angel Pons88521882020-01-05 20:21:20 +0100849 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100850 }
851
Angel Pons7c49cb82020-03-16 23:17:32 +0100852 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +0100853 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100854
855 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +0100856 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100857
Angel Pons88521882020-01-05 20:21:20 +0100858 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100859
860 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
861
Angel Pons7c49cb82020-03-16 23:17:32 +0100862 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100863 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100864
865 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100866 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
867 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
868 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
869 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200870
Angel Pons7c49cb82020-03-16 23:17:32 +0100871 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100872 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100873
Angel Pons7c49cb82020-03-16 23:17:32 +0100874 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100875 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100876 }
877}
878
Felix Held3b906032020-01-14 17:05:43 +0100879static const u32 lane_base[] = {
880 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
881 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
882 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100883};
884
Angel Pons88521882020-01-05 20:21:20 +0100885void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100886{
Angel Pons88521882020-01-05 20:21:20 +0100887 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100888 int lane;
889 int slotrank, slot;
890 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +0100891 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100892
893 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +0100894 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
895 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100896 }
897
898 for (slot = 0; slot < NUM_SLOTS; slot++)
899 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
900 case 0:
901 default:
Angel Pons88521882020-01-05 20:21:20 +0100902 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100903 break;
904 case 1:
Angel Pons88521882020-01-05 20:21:20 +0100905 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100906 ctrl->timings[channel][2 * slot + 0].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100907 break;
908 case 2:
Angel Pons88521882020-01-05 20:21:20 +0100909 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100910 ctrl->timings[channel][2 * slot + 1].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100911 break;
912 case 3:
Angel Pons88521882020-01-05 20:21:20 +0100913 pi_coding_ctrl[slot] =
914 (ctrl->timings[channel][2 * slot].pi_coding +
Angel Pons7c49cb82020-03-16 23:17:32 +0100915 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100916 break;
917 }
918
Angel Pons7c49cb82020-03-16 23:17:32 +0100919 /* Enable CMD XOVER */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100920 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons7c49cb82020-03-16 23:17:32 +0100921 reg32 |= (pi_coding_ctrl[0] & 0x3f) << 6;
922 reg32 |= (pi_coding_ctrl[0] & 0x40) << 9;
Angel Pons88521882020-01-05 20:21:20 +0100923 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100924 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
925
Angel Pons88521882020-01-05 20:21:20 +0100926 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100927
Angel Pons7c49cb82020-03-16 23:17:32 +0100928 /* Enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +0100929 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
930 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100931
932 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100933 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Angel Pons88521882020-01-05 20:21:20 +0100934 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100935 if (shift < 0)
936 shift = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100937
Angel Pons88521882020-01-05 20:21:20 +0100938 offset_pi_code = ctrl->pi_code_offset + shift;
Angel Pons7c49cb82020-03-16 23:17:32 +0100939
940 /* Set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +0100941 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
942 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100943 }
944
Angel Pons88521882020-01-05 20:21:20 +0100945 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
946 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100947
Angel Pons88521882020-01-05 20:21:20 +0100948 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +0100949 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100950
Angel Pons88521882020-01-05 20:21:20 +0100951 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100952
953 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100954 int post_timA_min_high = 7, pre_timA_min_high = 7;
955 int post_timA_max_high = 0, pre_timA_max_high = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100956 int shift_402x = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100957 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100958
959 if (shift < 0)
960 shift = 0;
961
962 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +0200963 post_timA_min_high = MIN(post_timA_min_high,
964 (ctrl->timings[channel][slotrank].lanes[lane].
965 timA + shift) >> 6);
966 pre_timA_min_high = MIN(pre_timA_min_high,
967 ctrl->timings[channel][slotrank].lanes[lane].
968 timA >> 6);
969 post_timA_max_high = MAX(post_timA_max_high,
970 (ctrl->timings[channel][slotrank].lanes[lane].
971 timA + shift) >> 6);
972 pre_timA_max_high = MAX(pre_timA_max_high,
973 ctrl->timings[channel][slotrank].lanes[lane].
974 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100975 }
976
977 if (pre_timA_max_high - pre_timA_min_high <
978 post_timA_max_high - post_timA_min_high)
979 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +0100980
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100981 else if (pre_timA_max_high - pre_timA_min_high >
982 post_timA_max_high - post_timA_min_high)
983 shift_402x = -1;
984
Felix Helddee167e2019-12-30 17:30:16 +0100985 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +0100986 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100987 post_timA_min_high) << (4 * slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +0100988
Angel Pons88521882020-01-05 20:21:20 +0100989 reg_roundtrip_latency |=
990 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100991 shift_402x) << (8 * slotrank);
992
993 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +0100994 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100995 (((ctrl->timings[channel][slotrank].lanes[lane].
996 timA + shift) & 0x3f)
997 |
998 ((ctrl->timings[channel][slotrank].lanes[lane].
999 rising + shift) << 8)
1000 |
1001 (((ctrl->timings[channel][slotrank].lanes[lane].
1002 timA + shift -
1003 (post_timA_min_high << 6)) & 0x1c0) << 10)
1004 | ((ctrl->timings[channel][slotrank].lanes[lane].
1005 falling + shift) << 20));
1006
Felix Heldfb19c8a2020-01-14 21:27:59 +01001007 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001008 (((ctrl->timings[channel][slotrank].lanes[lane].
1009 timC + shift) & 0x3f)
1010 |
1011 (((ctrl->timings[channel][slotrank].lanes[lane].
1012 timB + shift) & 0x3f) << 8)
1013 |
1014 (((ctrl->timings[channel][slotrank].lanes[lane].
1015 timB + shift) & 0x1c0) << 9)
1016 |
1017 (((ctrl->timings[channel][slotrank].lanes[lane].
1018 timC + shift) & 0x40) << 13));
1019 }
1020 }
Angel Pons88521882020-01-05 20:21:20 +01001021 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1022 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001023}
1024
Angel Pons88521882020-01-05 20:21:20 +01001025static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001026{
Angel Pons88521882020-01-05 20:21:20 +01001027 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001028
1029 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001030 write MR3 MPR enable
1031 in this mode only RD and RDA are allowed
1032 all reads return a predefined pattern */
1033 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1034 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = (0xc01 | (ctrl->tMOD << 16));
Angel Pons88521882020-01-05 20:21:20 +01001035 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001036 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001037
1038 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001039 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1040 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4040c01;
Angel Pons88521882020-01-05 20:21:20 +01001041 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001042 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001043
1044 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001045 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1046 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x100f | ((ctrl->CAS + 36) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001047 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001048 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001049
1050 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001051 write MR3 MPR disable */
1052 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1053 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001054 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001055 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001056
Angel Pons7c49cb82020-03-16 23:17:32 +01001057 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001058 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001059
Angel Pons88521882020-01-05 20:21:20 +01001060 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001061}
1062
Angel Pons7c49cb82020-03-16 23:17:32 +01001063static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank, int lane)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001064{
1065 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Angel Pons7c49cb82020-03-16 23:17:32 +01001066
1067 return (MCHBAR32(lane_base[lane] +
1068 GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >> (timA % 32)) & 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001069}
1070
1071struct run {
1072 int middle;
1073 int end;
1074 int start;
1075 int all;
1076 int length;
1077};
1078
1079static struct run get_longest_zero_run(int *seq, int sz)
1080{
1081 int i, ls;
1082 int bl = 0, bs = 0;
1083 struct run ret;
1084
1085 ls = 0;
1086 for (i = 0; i < 2 * sz; i++)
1087 if (seq[i % sz]) {
1088 if (i - ls > bl) {
1089 bl = i - ls;
1090 bs = ls;
1091 }
1092 ls = i + 1;
1093 }
1094 if (bl == 0) {
1095 ret.middle = sz / 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001096 ret.start = 0;
1097 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001098 ret.length = sz;
Angel Pons7c49cb82020-03-16 23:17:32 +01001099 ret.all = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001100 return ret;
1101 }
1102
Angel Pons7c49cb82020-03-16 23:17:32 +01001103 ret.start = bs % sz;
1104 ret.end = (bs + bl - 1) % sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001105 ret.middle = (bs + (bl - 1) / 2) % sz;
1106 ret.length = bl;
Angel Pons7c49cb82020-03-16 23:17:32 +01001107 ret.all = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001108
1109 return ret;
1110}
1111
Angel Pons7c49cb82020-03-16 23:17:32 +01001112static void discover_timA_coarse(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001113{
1114 int timA;
1115 int statistics[NUM_LANES][128];
1116 int lane;
1117
1118 for (timA = 0; timA < 128; timA++) {
1119 FOR_ALL_LANES {
1120 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1121 }
1122 program_timings(ctrl, channel);
1123
1124 test_timA(ctrl, channel, slotrank);
1125
1126 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001127 statistics[lane][timA] = !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001128 }
1129 }
1130 FOR_ALL_LANES {
1131 struct run rn = get_longest_zero_run(statistics[lane], 128);
1132 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1133 upperA[lane] = rn.end;
1134 if (upperA[lane] < rn.middle)
1135 upperA[lane] += 128;
Angel Pons7c49cb82020-03-16 23:17:32 +01001136
Patrick Rudolph368b6152016-11-25 16:36:52 +01001137 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001138 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001139 }
1140}
1141
Angel Pons7c49cb82020-03-16 23:17:32 +01001142static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001143{
1144 int timA_delta;
1145 int statistics[NUM_LANES][51];
1146 int lane, i;
1147
1148 memset(statistics, 0, sizeof(statistics));
1149
1150 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01001151
1152 FOR_ALL_LANES {
1153 ctrl->timings[channel][slotrank].lanes[lane].timA
1154 = upperA[lane] + timA_delta + 0x40;
1155 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001156 program_timings(ctrl, channel);
1157
1158 for (i = 0; i < 100; i++) {
1159 test_timA(ctrl, channel, slotrank);
1160 FOR_ALL_LANES {
1161 statistics[lane][timA_delta + 25] +=
Angel Pons7c49cb82020-03-16 23:17:32 +01001162 does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001163 }
1164 }
1165 }
1166 FOR_ALL_LANES {
1167 int last_zero, first_all;
1168
1169 for (last_zero = -25; last_zero <= 25; last_zero++)
1170 if (statistics[lane][last_zero + 25])
1171 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01001172
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001173 last_zero--;
1174 for (first_all = -25; first_all <= 25; first_all++)
1175 if (statistics[lane][first_all + 25] == 100)
1176 break;
1177
Angel Pons7c49cb82020-03-16 23:17:32 +01001178 printram("lane %d: %d, %d\n", lane, last_zero, first_all);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001179
1180 ctrl->timings[channel][slotrank].lanes[lane].timA =
Angel Pons7c49cb82020-03-16 23:17:32 +01001181 (last_zero + first_all) / 2 + upperA[lane];
1182
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001183 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01001184 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001185 }
1186}
1187
Angel Pons891f2bc2020-01-10 01:27:28 +01001188static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001189{
1190 int works[NUM_LANES];
1191 int lane;
Angel Pons7c49cb82020-03-16 23:17:32 +01001192
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001193 while (1) {
1194 int all_works = 1, some_works = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001195
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001196 program_timings(ctrl, channel);
1197 test_timA(ctrl, channel, slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001198
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001199 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001200 works[lane] = !does_lane_work(ctrl, channel, slotrank, lane);
1201
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001202 if (works[lane])
1203 some_works = 1;
1204 else
1205 all_works = 0;
1206 }
1207 if (all_works)
1208 return 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001209
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001210 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001211 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001212 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1213 channel, slotrank);
1214 return MAKE_ERR;
1215 }
Angel Pons88521882020-01-05 20:21:20 +01001216 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001217 printram("4024 -= 2;\n");
1218 continue;
1219 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001220 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001221 printram("4028 += 2;\n");
Angel Pons7c49cb82020-03-16 23:17:32 +01001222
Felix Heldef4fe3e2019-12-31 14:15:05 +01001223 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001224 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1225 channel, slotrank);
1226 return MAKE_ERR;
1227 }
1228 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001229 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001230 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001231 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001232 }
1233 }
1234 return 0;
1235}
1236
1237struct timA_minmax {
1238 int timA_min_high, timA_max_high;
1239};
1240
Angel Pons88521882020-01-05 20:21:20 +01001241static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001242 struct timA_minmax *mnmx)
1243{
1244 int lane;
1245 mnmx->timA_min_high = 7;
1246 mnmx->timA_max_high = 0;
1247
1248 FOR_ALL_LANES {
1249 if (mnmx->timA_min_high >
1250 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1251 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001252 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001253 if (mnmx->timA_max_high <
1254 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1255 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001256 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001257 }
1258}
1259
Angel Pons88521882020-01-05 20:21:20 +01001260static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001261 struct timA_minmax *mnmx)
1262{
1263 struct timA_minmax post;
1264 int shift_402x = 0;
1265
Angel Pons7c49cb82020-03-16 23:17:32 +01001266 /* Get changed maxima */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001267 pre_timA_change(ctrl, channel, slotrank, &post);
1268
1269 if (mnmx->timA_max_high - mnmx->timA_min_high <
1270 post.timA_max_high - post.timA_min_high)
1271 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001272
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001273 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1274 post.timA_max_high - post.timA_min_high)
1275 shift_402x = -1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001276
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001277 else
1278 shift_402x = 0;
1279
Felix Heldef4fe3e2019-12-31 14:15:05 +01001280 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001281 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001282 printram("4024 += %d;\n", shift_402x);
1283 printram("4028 += %d;\n", shift_402x);
1284}
1285
Angel Pons7c49cb82020-03-16 23:17:32 +01001286/*
1287 * Compensate the skew between DQS and DQs.
1288 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001289 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1290 * The controller has to measure and compensate this skew for every byte-lane. By delaying
Angel Pons7c49cb82020-03-16 23:17:32 +01001291 * either all DQ signals or DQS signal, a full phase shift can be introduced. It is assumed
Angel Pons891f2bc2020-01-10 01:27:28 +01001292 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001293 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001294 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1295 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1296 * over all possible values to do a full phase shift and issues read commands. With DQS and
Angel Pons7c49cb82020-03-16 23:17:32 +01001297 * DQ in phase the data being read is expected to alternate on every byte:
1298 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001299 * 0xFF 0x00 0xFF ...
Angel Pons7c49cb82020-03-16 23:17:32 +01001300 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001301 * Once the controller has detected this pattern a bit in the result register is set for the
1302 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001303 */
Angel Pons88521882020-01-05 20:21:20 +01001304int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001305{
1306 int channel, slotrank, lane;
1307 int err;
1308
1309 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1310 int all_high, some_high;
1311 int upperA[NUM_LANES];
1312 struct timA_minmax mnmx;
1313
Angel Pons88521882020-01-05 20:21:20 +01001314 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001315
Felix Held2bb3cdf2018-07-28 00:23:59 +02001316 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001317 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1318 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001319 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001320 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001321
Angel Pons7c49cb82020-03-16 23:17:32 +01001322 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001323 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001324
Angel Pons88521882020-01-05 20:21:20 +01001325 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001326
Felix Heldef4fe3e2019-12-31 14:15:05 +01001327 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001328 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001329 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001330
Felix Held2bb3cdf2018-07-28 00:23:59 +02001331 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001332
Felix Held2bb3cdf2018-07-28 00:23:59 +02001333 all_high = 1;
1334 some_high = 0;
1335 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001336 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001337 some_high = 1;
1338 else
1339 all_high = 0;
1340 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001341
1342 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001343 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001344 printram("4028--;\n");
1345 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001346 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001347 upperA[lane] -= 0x40;
1348
1349 }
1350 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001351 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001352 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001353 printram("4024++;\n");
1354 printram("4028++;\n");
1355 }
1356
1357 program_timings(ctrl, channel);
1358
1359 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1360
1361 err = discover_402x(ctrl, channel, slotrank, upperA);
1362 if (err)
1363 return err;
1364
1365 post_timA_change(ctrl, channel, slotrank, &mnmx);
1366 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1367
1368 discover_timA_fine(ctrl, channel, slotrank, upperA);
1369
1370 post_timA_change(ctrl, channel, slotrank, &mnmx);
1371 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1372
1373 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001374 ctrl->timings[channel][slotrank].lanes[lane].timA -=
1375 mnmx.timA_min_high * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001376 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001377 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001378 printram("4028 -= %d;\n", mnmx.timA_min_high);
1379
1380 post_timA_change(ctrl, channel, slotrank, &mnmx);
1381
1382 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001383 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001384 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001385
1386 printram("final results:\n");
1387 FOR_ALL_LANES
Angel Pons7c49cb82020-03-16 23:17:32 +01001388 printram("Aval: %d, %d, %d: %x\n", channel, slotrank, lane,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001389 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001390
Angel Pons88521882020-01-05 20:21:20 +01001391 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001392
1393 toggle_io_reset();
1394 }
1395
1396 FOR_ALL_POPULATED_CHANNELS {
1397 program_timings(ctrl, channel);
1398 }
1399 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001400 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001401 }
1402 return 0;
1403}
1404
Angel Pons88521882020-01-05 20:21:20 +01001405static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001406{
1407 int lane;
1408
1409 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001410 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1411 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001412 }
1413
Angel Pons88521882020-01-05 20:21:20 +01001414 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001415
1416 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001417 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
1418 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
1419 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | 4 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001420 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | (6 << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +01001421 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001422
1423 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001424 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f207;
1425 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8041001;
Angel Pons88521882020-01-05 20:21:20 +01001426 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001427 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001428
1429 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01001430 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f201;
1431 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x80411f4;
Angel Pons88521882020-01-05 20:21:20 +01001432 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001433 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001434
1435 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001436 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f207;
1437 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
1438 0x08000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001439 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001440 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001441
Angel Pons7c49cb82020-03-16 23:17:32 +01001442 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001443 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001444
Angel Pons88521882020-01-05 20:21:20 +01001445 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001446
1447 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001448 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1449 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001450 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001451 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001452
1453 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001454 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f006;
1455 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
1456 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) | 8 | (ctrl->CAS << 16);
Angel Pons88521882020-01-05 20:21:20 +01001457 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001458 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001459
1460 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001461 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1462 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001463 0x40011f4 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001464 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001465 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001466
1467 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001468 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
1469 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001470 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001471 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001472
Angel Pons7c49cb82020-03-16 23:17:32 +01001473 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001474 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001475
Angel Pons88521882020-01-05 20:21:20 +01001476 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001477}
1478
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001479static void timC_threshold_process(int *data, const int count)
1480{
1481 int min = data[0];
1482 int max = min;
1483 int i;
1484 for (i = 1; i < count; i++) {
1485 if (min > data[i])
1486 min = data[i];
Angel Pons7c49cb82020-03-16 23:17:32 +01001487
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001488 if (max < data[i])
1489 max = data[i];
1490 }
Angel Pons7c49cb82020-03-16 23:17:32 +01001491 int threshold = min / 2 + max / 2;
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001492 for (i = 0; i < count; i++)
1493 data[i] = data[i] > threshold;
Angel Pons7c49cb82020-03-16 23:17:32 +01001494
Angel Pons891f2bc2020-01-10 01:27:28 +01001495 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001496}
1497
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001498static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1499{
1500 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01001501 int stats[NUM_LANES][MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001502 int lane;
1503
Angel Pons88521882020-01-05 20:21:20 +01001504 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001505
1506 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001507 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1508 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001509 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001510 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001511
Angel Pons7c49cb82020-03-16 23:17:32 +01001512 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001513 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001514
1515 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001516 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001517 program_timings(ctrl, channel);
1518
1519 test_timC(ctrl, channel, slotrank);
1520
1521 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001522 stats[lane][timC] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001523 }
1524 }
1525 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001526 struct run rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1527
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001528 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001529 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1530 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001531 /*
1532 * With command training not being done yet, the lane can be erroneous.
1533 * Take the average as reference and try again to find a run.
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001534 */
Angel Pons7c49cb82020-03-16 23:17:32 +01001535 timC_threshold_process(stats[lane], ARRAY_SIZE(stats[lane]));
1536 rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1537
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001538 if (rn.all || rn.length < 8) {
1539 printk(BIOS_EMERG, "timC recovery failed\n");
1540 return MAKE_ERR;
1541 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001542 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001543 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001544 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001545 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001546 }
1547 return 0;
1548}
1549
Angel Pons88521882020-01-05 20:21:20 +01001550static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001551{
1552 int channel, ret = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001553
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001554 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1555 ret++;
Angel Pons7c49cb82020-03-16 23:17:32 +01001556
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001557 return ret;
1558}
1559
Angel Pons88521882020-01-05 20:21:20 +01001560static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001561{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301562 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001563 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Angel Pons7c49cb82020-03-16 23:17:32 +01001564
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001565 for (j = 0; j < 16; j++)
1566 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
Angel Pons7c49cb82020-03-16 23:17:32 +01001567
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001568 sfence();
1569}
1570
Angel Pons88521882020-01-05 20:21:20 +01001571static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001572{
1573 int ret = 0;
1574 int channel;
1575 FOR_ALL_POPULATED_CHANNELS ret++;
1576 return ret;
1577}
1578
Angel Pons88521882020-01-05 20:21:20 +01001579static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001580{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301581 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001582 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301583 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01001584
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001585 for (j = 0; j < 16; j++)
1586 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
Angel Pons7c49cb82020-03-16 23:17:32 +01001587
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001588 for (j = 0; j < 16; j++)
1589 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
Angel Pons7c49cb82020-03-16 23:17:32 +01001590
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001591 sfence();
1592}
1593
Angel Pons88521882020-01-05 20:21:20 +01001594static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001595{
1596 int channel, slotrank, lane;
1597
1598 FOR_ALL_POPULATED_CHANNELS {
1599 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001600 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1601 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001602 }
1603
1604 program_timings(ctrl, channel);
1605
1606 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001607 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001608
1609 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001610 write MR3 MPR enable
1611 in this mode only RD and RDA are allowed
1612 all reads return a predefined pattern */
1613 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1614 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001615 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001616 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001617 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001618 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001619
1620 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001621 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1622 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001623 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001624 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001625
1626 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001627 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1628 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001629 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001630 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001631 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001632 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001633
1634 /* DRAM command MRS
1635 * write MR3 MPR disable */
Angel Pons7c49cb82020-03-16 23:17:32 +01001636 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1637 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001638 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001639 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001640 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001641 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001642
Angel Pons7c49cb82020-03-16 23:17:32 +01001643 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001644 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001645
Angel Pons88521882020-01-05 20:21:20 +01001646 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001647 }
1648
1649 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001650 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1651 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001652 }
1653
1654 program_timings(ctrl, channel);
1655
1656 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001657 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001658 /* DRAM command MRS
1659 * write MR3 MPR enable
1660 * in this mode only RD and RDA are allowed
1661 * all reads return a predefined pattern */
Angel Pons7c49cb82020-03-16 23:17:32 +01001662 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1663 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001664 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001665 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001666 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001667 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001668
1669 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001670 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1671 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001672 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001673 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001674
1675 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001676 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1677 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001678 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001679 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001680 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001681 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001682
1683 /* DRAM command MRS
1684 * write MR3 MPR disable */
Angel Pons7c49cb82020-03-16 23:17:32 +01001685 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1686 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001687 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001688 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001689 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001690 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001691
Angel Pons7c49cb82020-03-16 23:17:32 +01001692 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001693 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001694
Angel Pons88521882020-01-05 20:21:20 +01001695 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001696 }
1697 }
1698}
1699
Angel Pons88521882020-01-05 20:21:20 +01001700static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001701{
1702 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001703 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001704
Angel Pons88521882020-01-05 20:21:20 +01001705 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001706 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001707 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f207;
1708 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001709 0x8000c01 | ((ctrl->CWL + ctrl->tWLO) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001710 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 8 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001711 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001712
1713 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001714 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f107;
1715 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4000c01 | ((ctrl->CAS + 38) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001716 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01001717 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001718
Angel Pons7c49cb82020-03-16 23:17:32 +01001719 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001720 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001721
Angel Pons88521882020-01-05 20:21:20 +01001722 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001723
1724 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001725 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001726}
1727
1728static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1729{
1730 int timB;
1731 int statistics[NUM_LANES][128];
1732 int lane;
1733
Angel Pons88521882020-01-05 20:21:20 +01001734 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001735
1736 for (timB = 0; timB < 128; timB++) {
1737 FOR_ALL_LANES {
1738 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1739 }
1740 program_timings(ctrl, channel);
1741
1742 test_timB(ctrl, channel, slotrank);
1743
1744 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001745 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1746 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1747 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001748 }
1749 }
1750 FOR_ALL_LANES {
1751 struct run rn = get_longest_zero_run(statistics[lane], 128);
Angel Pons7c49cb82020-03-16 23:17:32 +01001752 /*
1753 * timC is a direct function of timB's 6 LSBs. Some tests increments the value
1754 * of timB by a small value, which might cause the 6-bit value to overflow if
1755 * it's close to 0x3f. Increment the value by a small offset if it's likely
1756 * to overflow, to make sure it won't overflow while running tests and bricks
1757 * the system due to a non matching timC.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001758 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001759 * TODO: find out why some tests (edge write discovery) increment timB.
1760 */
1761 if ((rn.start & 0x3f) == 0x3e)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001762 rn.start += 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001763 else if ((rn.start & 0x3f) == 0x3f)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001764 rn.start += 1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001765
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001766 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1767 if (rn.all) {
1768 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1769 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001770
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001771 return MAKE_ERR;
1772 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001773 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1774 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001775 }
1776 return 0;
1777}
1778
1779static int get_timB_high_adjust(u64 val)
1780{
1781 int i;
1782
1783 /* good */
1784 if (val == 0xffffffffffffffffLL)
1785 return 0;
1786
1787 if (val >= 0xf000000000000000LL) {
1788 /* needs negative adjustment */
1789 for (i = 0; i < 8; i++)
1790 if (val << (8 * (7 - i) + 4))
1791 return -i;
1792 } else {
1793 /* needs positive adjustment */
1794 for (i = 0; i < 8; i++)
1795 if (val >> (8 * (7 - i) + 4))
1796 return i;
1797 }
1798 return 8;
1799}
1800
Angel Pons88521882020-01-05 20:21:20 +01001801static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001802{
1803 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001804 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001805 FOR_ALL_POPULATED_CHANNELS {
1806 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001807 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001808 }
1809 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1810
Angel Pons88521882020-01-05 20:21:20 +01001811 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001812
Angel Pons88521882020-01-05 20:21:20 +01001813 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001814
1815 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001816 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
1817 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001818 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001819 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001820
1821 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001822 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f207;
1823 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8040c01;
Angel Pons88521882020-01-05 20:21:20 +01001824 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001825 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001826
1827 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01001828 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f201;
1829 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x8041003;
Angel Pons88521882020-01-05 20:21:20 +01001830 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001831 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x3e2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001832
1833 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001834 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f207;
1835 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001836 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001837 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001838 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001839
Angel Pons7c49cb82020-03-16 23:17:32 +01001840 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001841 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001842
Angel Pons88521882020-01-05 20:21:20 +01001843 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001844
1845 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001846 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1847 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | ((ctrl->tRP) << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01001848 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001849 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001850
1851 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001852 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f006;
1853 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0xc01 | ((ctrl->tRCD) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001854 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001855 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001856
1857 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001858 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x3f105;
1859 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x4000c01 | ((ctrl->tRP +
Angel Pons88521882020-01-05 20:21:20 +01001860 ctrl->timings[channel][slotrank].roundtrip_latency +
Felix Heldef4fe3e2019-12-31 14:15:05 +01001861 ctrl->timings[channel][slotrank].io_latency) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001862 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60008;
Angel Pons7c49cb82020-03-16 23:17:32 +01001863 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001864
Angel Pons7c49cb82020-03-16 23:17:32 +01001865 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001866 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Felix Held9cf1dd22018-07-31 14:52:40 +02001867
Angel Pons88521882020-01-05 20:21:20 +01001868 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001869 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001870 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01001871 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01001872 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001873 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
1874 ctrl->timings[channel][slotrank].lanes[lane].timB +=
1875 get_timB_high_adjust(res) * 64;
1876
1877 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01001878 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
1879 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001880 }
1881 }
Angel Pons88521882020-01-05 20:21:20 +01001882 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001883}
1884
Angel Pons88521882020-01-05 20:21:20 +01001885static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001886{
1887 int slotrank;
1888
Angel Pons88521882020-01-05 20:21:20 +01001889 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001890
1891 /* choose an existing rank. */
1892 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
1893
1894 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001895 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
1896 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01001897 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001898 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001899
Angel Pons7c49cb82020-03-16 23:17:32 +01001900 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001901 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001902
Angel Pons88521882020-01-05 20:21:20 +01001903 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001904}
1905
Angel Pons7c49cb82020-03-16 23:17:32 +01001906/*
1907 * Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001908 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001909 * Since DDR3 uses a fly-by topology, the data and strobes signals reach the chips at different
1910 * times with respect to command, address and clock signals. By delaying either all DQ/DQS or
1911 * all CMD/ADDR/CLK signals, a full phase shift can be introduced. It is assumed that the
1912 * CLK/ADDR/CMD signals have the same routing delay.
1913 *
1914 * To find the required phase shift the DRAM is placed in "write leveling" mode. In this mode,
1915 * the DRAM-chip samples the CLK on every DQS edge and feeds back the sampled value on the data
1916 * lanes (DQ).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001917 */
Angel Pons88521882020-01-05 20:21:20 +01001918int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001919{
1920 int channel, slotrank, lane;
1921 int err;
1922
1923 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001924 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001925
1926 FOR_ALL_POPULATED_CHANNELS {
1927 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001928 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001929 }
1930
Angel Pons7c49cb82020-03-16 23:17:32 +01001931 /* Refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01001932 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001933 FOR_ALL_POPULATED_CHANNELS {
1934 write_op(ctrl, channel);
1935 }
1936
Angel Pons7c49cb82020-03-16 23:17:32 +01001937 /* Enable write leveling on all ranks
1938 Disable all DQ outputs
1939 Only NOP is allowed in this mode */
1940 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
1941 write_mrreg(ctrl, channel, slotrank, 1,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001942 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001943
Angel Pons88521882020-01-05 20:21:20 +01001944 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001945
1946 toggle_io_reset();
1947
Angel Pons7c49cb82020-03-16 23:17:32 +01001948 /* Set any valid value for timB, it gets corrected later */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001949 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1950 err = discover_timB(ctrl, channel, slotrank);
1951 if (err)
1952 return err;
1953 }
1954
Angel Pons7c49cb82020-03-16 23:17:32 +01001955 /* Disable write leveling on all ranks */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001956 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
Angel Pons7c49cb82020-03-16 23:17:32 +01001957 write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001958
Angel Pons88521882020-01-05 20:21:20 +01001959 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001960
1961 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001962 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001963
Angel Pons7c49cb82020-03-16 23:17:32 +01001964 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01001965 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001966
1967 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001968 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
1969 MCHBAR32(IOSAV_STATUS_ch(channel));
1970 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001971
1972 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01001973 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
1974 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
Angel Pons88521882020-01-05 20:21:20 +01001975 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001976 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001977
Angel Pons7c49cb82020-03-16 23:17:32 +01001978 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001979 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001980
Angel Pons88521882020-01-05 20:21:20 +01001981 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001982 }
1983
1984 toggle_io_reset();
1985
1986 printram("CPE\n");
1987 precharge(ctrl);
1988 printram("CPF\n");
1989
1990 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001991 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001992 }
1993
1994 FOR_ALL_POPULATED_CHANNELS {
1995 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01001996 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001997 }
1998
1999 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2000 err = discover_timC(ctrl, channel, slotrank);
2001 if (err)
2002 return err;
2003 }
2004
2005 FOR_ALL_POPULATED_CHANNELS
2006 program_timings(ctrl, channel);
2007
2008 /* measure and adjust timB timings */
2009 adjust_high_timB(ctrl);
2010
2011 FOR_ALL_POPULATED_CHANNELS
2012 program_timings(ctrl, channel);
2013
2014 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002015 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002016 }
2017 return 0;
2018}
2019
Angel Pons88521882020-01-05 20:21:20 +01002020static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002021{
2022 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2023 int timC_delta;
2024 int lanes_ok = 0;
2025 int ctr = 0;
2026 int lane;
2027
2028 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2029 FOR_ALL_LANES {
2030 ctrl->timings[channel][slotrank].lanes[lane].timC =
2031 saved_rt.lanes[lane].timC + timC_delta;
2032 }
2033 program_timings(ctrl, channel);
2034 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002035 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002036 }
2037
Angel Pons88521882020-01-05 20:21:20 +01002038 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002039
Angel Pons88521882020-01-05 20:21:20 +01002040 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002041 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002042 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
2043 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002044 ((MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002045 | 8 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002046 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002047 (slotrank << 24) | ctr | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002048 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Felix Held9fe248f2018-07-31 20:59:45 +02002049
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002050 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002051 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2052 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002053 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002054 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
2055 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
Angel Pons7c49cb82020-03-16 23:17:32 +01002056 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002057
2058 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002059 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2060 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002061 0x4001020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002062 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
2063 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
Angel Pons7c49cb82020-03-16 23:17:32 +01002064 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002065
2066 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002067 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2068 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xf1001;
Angel Pons88521882020-01-05 20:21:20 +01002069 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002070 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002071
Angel Pons7c49cb82020-03-16 23:17:32 +01002072 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002073 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002074
Angel Pons88521882020-01-05 20:21:20 +01002075 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002076 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002077 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002078
2079 if (r32 == 0)
2080 lanes_ok |= 1 << lane;
2081 }
2082 ctr++;
2083 if (lanes_ok == ((1 << NUM_LANES) - 1))
2084 break;
2085 }
2086
2087 ctrl->timings[channel][slotrank] = saved_rt;
2088
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002089 return lanes_ok != ((1 << NUM_LANES) - 1);
2090}
2091
Angel Pons88521882020-01-05 20:21:20 +01002092static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002093{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302094 unsigned int i, j;
Angel Pons7c49cb82020-03-16 23:17:32 +01002095 unsigned int offset = get_precedening_channels(ctrl, channel) * 0x40;
2096 unsigned int step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002097
2098 if (patno) {
2099 u8 base8 = 0x80 >> ((patno - 1) % 8);
2100 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2101 for (i = 0; i < 32; i++) {
2102 for (j = 0; j < 16; j++) {
2103 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01002104
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002105 if (invert[patno - 1][i] & (1 << (j / 2)))
2106 val = ~val;
Angel Pons7c49cb82020-03-16 23:17:32 +01002107
2108 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002109 }
2110 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002111 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002112 for (i = 0; i < ARRAY_SIZE(pattern); i++) {
2113 for (j = 0; j < 16; j++) {
2114 const u32 val = pattern[i][j];
2115 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
2116 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002117 }
2118 sfence();
2119 }
2120}
2121
Angel Pons88521882020-01-05 20:21:20 +01002122static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002123{
2124 int channel, slotrank;
2125
2126 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002127 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002128
Angel Pons7c49cb82020-03-16 23:17:32 +01002129 /* Choose an existing rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002130 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2131
2132 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01002133 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
2134 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002135 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002136 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002137
Angel Pons7c49cb82020-03-16 23:17:32 +01002138 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002139 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002140
Angel Pons88521882020-01-05 20:21:20 +01002141 wait_for_iosav(channel);
2142 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002143 }
2144
2145 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002146 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002147 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002148 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002149
2150 /* choose an existing rank. */
2151 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2152
2153 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01002154 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
2155 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002156 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002157 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
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 */
2318 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2319 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002320 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002321 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002322
2323 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002324 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2325 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x40411f4;
Angel Pons88521882020-01-05 20:21:20 +01002326 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002327 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002328
2329 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002330 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2331 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002332 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002333 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002334
2335 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002336 MR3 disable MPR */
2337 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2338 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002339 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002340 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002341
Angel Pons7c49cb82020-03-16 23:17:32 +01002342 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002343 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002344
Angel Pons88521882020-01-05 20:21:20 +01002345 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002346
2347 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002348 stats[lane][edge] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002349 }
2350 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002351
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002352 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002353 struct run rn = get_longest_zero_run(stats[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002354 edges[lane] = rn.middle;
Angel Pons7c49cb82020-03-16 23:17:32 +01002355
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002356 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002357 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n", channel,
2358 slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002359 return MAKE_ERR;
2360 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002361 printram("eval %d, %d, %d: %02x\n", channel, slotrank, lane, edges[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002362 }
2363 return 0;
2364}
2365
2366int discover_edges(ramctr_timing *ctrl)
2367{
2368 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2369 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2370 int channel, slotrank, lane;
2371 int err;
2372
Angel Pons88521882020-01-05 20:21:20 +01002373 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002374
2375 toggle_io_reset();
2376
2377 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002378 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002379 }
2380
2381 FOR_ALL_POPULATED_CHANNELS {
2382 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002383 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002384 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002385 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002386 }
2387
2388 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002389 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2390 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002391 }
2392
2393 program_timings(ctrl, channel);
2394
2395 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002396 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002397
2398 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002399 MR3 enable MPR
2400 write MR3 MPR enable
2401 in this mode only RD and RDA are allowed
2402 all reads return a predefined pattern */
2403 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2404 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002405 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002406 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002407 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002408 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002409
2410 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002411 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2412 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01002413 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002414 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002415
2416 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002417 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2418 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002419 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002420 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002421 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002422 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002423
2424 /* DRAM command MRS
2425 * MR3 disable MPR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002426 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2427 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002428 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002429 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002430 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002431 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02002432
Angel Pons7c49cb82020-03-16 23:17:32 +01002433 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002434 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002435
Angel Pons88521882020-01-05 20:21:20 +01002436 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002437 }
2438
2439 /* XXX: check any measured value ? */
2440
2441 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002442 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
Angel Pons7c49cb82020-03-16 23:17:32 +01002443 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002444 }
2445
2446 program_timings(ctrl, channel);
2447
2448 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002449 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002450
2451 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002452 MR3 enable MPR
2453 write MR3 MPR enable
2454 in this mode only RD and RDA are allowed
2455 all reads return a predefined pattern */
2456 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2457 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002458 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002459 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002460 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002461 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002462
2463 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002464 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2465 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons88521882020-01-05 20:21:20 +01002466 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Angel Pons63ae8de2020-01-10 02:03:47 +01002467 (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002468 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002469
2470 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002471 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2472 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002473 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002474 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002475 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002476 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002477
2478 /* DRAM command MRS
2479 * MR3 disable MPR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002480 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2481 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002482 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002483 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002484 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002485 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002486
Angel Pons7c49cb82020-03-16 23:17:32 +01002487 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002488 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002489
Angel Pons88521882020-01-05 20:21:20 +01002490 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002491 }
2492
2493 /* XXX: check any measured value ? */
2494
2495 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002496 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002497 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002498 }
2499
2500 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002501 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002502 }
2503
Angel Pons0c3936e2020-03-22 12:49:27 +01002504 /*
2505 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2506 * also use a single loop. It would seem that it is a debugging configuration.
2507 */
Angel Pons88521882020-01-05 20:21:20 +01002508 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2509 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002510
2511 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2512 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002513 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002514 if (err)
2515 return err;
2516 }
2517
Angel Pons88521882020-01-05 20:21:20 +01002518 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2519 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002520
2521 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2522 err = discover_edges_real(ctrl, channel, slotrank,
2523 rising_edges[channel][slotrank]);
2524 if (err)
2525 return err;
2526 }
2527
Angel Pons88521882020-01-05 20:21:20 +01002528 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002529
2530 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2531 ctrl->timings[channel][slotrank].lanes[lane].falling =
2532 falling_edges[channel][slotrank][lane];
2533 ctrl->timings[channel][slotrank].lanes[lane].rising =
2534 rising_edges[channel][slotrank][lane];
2535 }
2536
2537 FOR_ALL_POPULATED_CHANNELS {
2538 program_timings(ctrl, channel);
2539 }
2540
2541 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002542 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002543 }
2544 return 0;
2545}
2546
Angel Pons7c49cb82020-03-16 23:17:32 +01002547static int discover_edges_write_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002548{
2549 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002550 u32 raw_stats[MAX_EDGE_TIMING + 1];
2551 int stats[MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002552 const int reg3000b24[] = { 0, 0xc, 0x2c };
2553 int lane, i;
2554 int lower[NUM_LANES];
2555 int upper[NUM_LANES];
2556 int pat;
2557
2558 FOR_ALL_LANES {
2559 lower[lane] = 0;
2560 upper[lane] = MAX_EDGE_TIMING;
2561 }
2562
2563 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002564 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002565 printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
2566
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002567 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2568 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002569 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002570 printram("using pattern %d\n", pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002571
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002572 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2573 FOR_ALL_LANES {
2574 ctrl->timings[channel][slotrank].lanes[lane].
2575 rising = edge;
2576 ctrl->timings[channel][slotrank].lanes[lane].
2577 falling = edge;
2578 }
2579 program_timings(ctrl, channel);
2580
2581 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002582 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2583 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002584 }
Angel Pons88521882020-01-05 20:21:20 +01002585 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002586
2587 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002588 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
2589 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002590 0x4 | (ctrl->tRCD << 16) |
Angel Pons891f2bc2020-01-10 01:27:28 +01002591 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10);
Angel Pons88521882020-01-05 20:21:20 +01002592 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002593 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002594 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002595
2596 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002597 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2598 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8005020 |
Felix Held2bb3cdf2018-07-28 00:23:59 +02002599 ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002600 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002601 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002602 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002603
2604 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002605 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2606 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002607 0x4005020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002608 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002609 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002610 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002611
2612 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002613 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2614 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002615 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002616 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002617 (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002618 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002619
Angel Pons7c49cb82020-03-16 23:17:32 +01002620 /* Execute command queue */
2621 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002622
Angel Pons88521882020-01-05 20:21:20 +01002623 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002624 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002625 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002626 }
2627
Angel Pons7c49cb82020-03-16 23:17:32 +01002628 /* FIXME: This register only exists on Ivy Bridge */
2629 raw_stats[edge] = MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002630 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002631
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002632 FOR_ALL_LANES {
2633 struct run rn;
2634 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
Angel Pons7c49cb82020-03-16 23:17:32 +01002635 stats[edge] = !!(raw_stats[edge] & (1 << lane));
2636
2637 rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
2638
2639 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, "
2640 "0x%02x-0x%02x\n", channel, slotrank, i, rn.start,
2641 rn.middle, rn.end, rn.start + ctrl->edge_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002642 rn.end - ctrl->edge_offset[i]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002643
2644 lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
2645 upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
2646
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002647 edges[lane] = (lower[lane] + upper[lane]) / 2;
2648 if (rn.all || (lower[lane] > upper[lane])) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002649 printk(BIOS_EMERG, "edge write discovery failed: "
2650 "%d, %d, %d\n", channel, slotrank, lane);
2651
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002652 return MAKE_ERR;
2653 }
2654 }
2655 }
2656 }
2657
Angel Pons88521882020-01-05 20:21:20 +01002658 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002659 printram("CPA\n");
2660 return 0;
2661}
2662
2663int discover_edges_write(ramctr_timing *ctrl)
2664{
2665 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
Angel Pons7c49cb82020-03-16 23:17:32 +01002666 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2667 int channel, slotrank, lane, err;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002668
Angel Pons7c49cb82020-03-16 23:17:32 +01002669 /*
2670 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2671 * also use a single loop. It would seem that it is a debugging configuration.
2672 */
Angel Pons88521882020-01-05 20:21:20 +01002673 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2674 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002675
2676 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2677 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002678 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002679 if (err)
2680 return err;
2681 }
2682
Angel Pons88521882020-01-05 20:21:20 +01002683 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2684 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002685
2686 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2687 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002688 rising_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002689 if (err)
2690 return err;
2691 }
2692
Angel Pons88521882020-01-05 20:21:20 +01002693 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002694
2695 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2696 ctrl->timings[channel][slotrank].lanes[lane].falling =
Angel Pons7c49cb82020-03-16 23:17:32 +01002697 falling_edges[channel][slotrank][lane];
2698
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002699 ctrl->timings[channel][slotrank].lanes[lane].rising =
Angel Pons7c49cb82020-03-16 23:17:32 +01002700 rising_edges[channel][slotrank][lane];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002701 }
2702
2703 FOR_ALL_POPULATED_CHANNELS
2704 program_timings(ctrl, channel);
2705
2706 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002707 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002708 }
2709 return 0;
2710}
2711
2712static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2713{
Angel Pons88521882020-01-05 20:21:20 +01002714 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002715
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002716 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002717 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0001f006;
2718 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002719 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | (ctrl->tRCD << 16) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01002720 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2721 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x0244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002722
2723 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002724 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2725 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002726 0x80011e0 | ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002727 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002728 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002729
2730 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002731 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2732 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x40011e0 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002733 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002734 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002735
2736 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002737 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2738 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x1001 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002739 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002740 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002741
Angel Pons7c49cb82020-03-16 23:17:32 +01002742 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002743 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002744
Angel Pons88521882020-01-05 20:21:20 +01002745 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002746}
2747
2748int discover_timC_write(ramctr_timing *ctrl)
2749{
Angel Pons7c49cb82020-03-16 23:17:32 +01002750 const u8 rege3c_b24[3] = { 0, 0x0f, 0x2f };
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002751 int i, pat;
2752
2753 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2754 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2755 int channel, slotrank, lane;
2756
2757 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2758 lower[channel][slotrank][lane] = 0;
2759 upper[channel][slotrank][lane] = MAX_TIMC;
2760 }
2761
Angel Pons88521882020-01-05 20:21:20 +01002762 /*
2763 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2764 * FIXME: This must only be done on Ivy Bridge.
2765 */
2766 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002767 printram("discover timC write:\n");
2768
2769 for (i = 0; i < 3; i++)
2770 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002771
2772 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
2773 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel),
2774 ~0x3f000000, rege3c_b24[i] << 24);
2775
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002776 udelay(2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002777
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002778 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2779 FOR_ALL_POPULATED_RANKS {
2780 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01002781 u32 raw_stats[MAX_TIMC + 1];
2782 int stats[MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002783
2784 /* Make sure rn.start < rn.end */
Angel Pons7c49cb82020-03-16 23:17:32 +01002785 stats[MAX_TIMC] = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002786
2787 fill_pattern5(ctrl, channel, pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002788 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
2789
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002790 for (timC = 0; timC < MAX_TIMC; timC++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002791 FOR_ALL_LANES {
2792 ctrl->timings[channel][slotrank]
2793 .lanes[lane].timC = timC;
2794 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002795 program_timings(ctrl, channel);
2796
2797 test_timC_write (ctrl, channel, slotrank);
2798
Angel Pons7c49cb82020-03-16 23:17:32 +01002799 /* FIXME: Another IVB-only register! */
2800 raw_stats[timC] =
Angel Pons1aba2a32020-01-05 22:31:41 +01002801 MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002802 }
2803 FOR_ALL_LANES {
2804 struct run rn;
Angel Pons7c49cb82020-03-16 23:17:32 +01002805 for (timC = 0; timC < MAX_TIMC; timC++) {
2806 stats[timC] = !!(raw_stats[timC]
2807 & (1 << lane));
2808 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002809
Angel Pons7c49cb82020-03-16 23:17:32 +01002810 rn = get_longest_zero_run(stats, MAX_TIMC + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002811 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002812 printk(BIOS_EMERG,
2813 "timC write discovery failed: "
2814 "%d, %d, %d\n", channel,
2815 slotrank, lane);
2816
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002817 return MAKE_ERR;
2818 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002819 printram("timC: %d, %d, %d: "
2820 "0x%02x-0x%02x-0x%02x, "
2821 "0x%02x-0x%02x\n", channel, slotrank,
2822 i, rn.start, rn.middle, rn.end,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002823 rn.start + ctrl->timC_offset[i],
Angel Pons7c49cb82020-03-16 23:17:32 +01002824 rn.end - ctrl->timC_offset[i]);
2825
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002826 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002827 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002828 lower[channel][slotrank][lane]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002829
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002830 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002831 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002832 upper[channel][slotrank][lane]);
2833
2834 }
2835 }
2836 }
2837 }
2838
2839 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002840 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
Angel Pons88521882020-01-05 20:21:20 +01002841 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002842 udelay(2);
2843 }
2844
Angel Pons88521882020-01-05 20:21:20 +01002845 /*
2846 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2847 * FIXME: This must only be done on Ivy Bridge.
2848 */
2849 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002850
2851 printram("CPB\n");
2852
2853 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002854 printram("timC %d, %d, %d: %x\n", channel, slotrank, lane,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002855 (lower[channel][slotrank][lane] +
2856 upper[channel][slotrank][lane]) / 2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002857
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002858 ctrl->timings[channel][slotrank].lanes[lane].timC =
2859 (lower[channel][slotrank][lane] +
2860 upper[channel][slotrank][lane]) / 2;
2861 }
2862 FOR_ALL_POPULATED_CHANNELS {
2863 program_timings(ctrl, channel);
2864 }
2865 return 0;
2866}
2867
Angel Pons88521882020-01-05 20:21:20 +01002868void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002869{
2870 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002871 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002872
2873 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2874 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002875 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002876 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002877 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01002878 printram("normalize %d, %d, %d: mat %d\n",
2879 channel, slotrank, lane, mat);
2880
Felix Heldef4fe3e2019-12-31 14:15:05 +01002881 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01002882 printram("normalize %d, %d, %d: delta %d\n",
2883 channel, slotrank, lane, delta);
2884
Angel Pons88521882020-01-05 20:21:20 +01002885 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01002886 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002887 }
2888
2889 FOR_ALL_POPULATED_CHANNELS {
2890 program_timings(ctrl, channel);
2891 }
2892}
2893
Angel Pons88521882020-01-05 20:21:20 +01002894void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002895{
2896 int channel, slotrank;
2897
2898 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002899 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002900 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01002901 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002902 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002903 }
2904}
2905
2906int channel_test(ramctr_timing *ctrl)
2907{
2908 int channel, slotrank, lane;
2909
2910 slotrank = 0;
2911 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002912 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01002913 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002914 return MAKE_ERR;
2915 }
2916 FOR_ALL_POPULATED_CHANNELS {
2917 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
2918
Angel Pons88521882020-01-05 20:21:20 +01002919 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002920 }
2921
2922 for (slotrank = 0; slotrank < 4; slotrank++)
2923 FOR_ALL_CHANNELS
2924 if (ctrl->rankmap[channel] & (1 << slotrank)) {
2925 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002926 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
2927 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002928 }
Angel Pons88521882020-01-05 20:21:20 +01002929 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002930
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002931 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002932 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0001f006;
2933 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x0028a004;
Angel Pons891f2bc2020-01-10 01:27:28 +01002934 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x00060000 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002935 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x00000244;
Felix Held9cf1dd22018-07-31 14:52:40 +02002936
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002937 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002938 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x0001f201;
2939 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x08281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002940 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002941 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002942
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002943 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002944 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x0001f105;
2945 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x04281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002946 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002947 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002948
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002949 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002950 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x0001f002;
2951 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x00280c01;
Angel Pons891f2bc2020-01-10 01:27:28 +01002952 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = 0x00060400 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002953 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x00000240;
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
Angel Pons88521882020-01-05 20:21:20 +01002969void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002970{
2971 int channel;
2972
Angel Pons7c49cb82020-03-16 23:17:32 +01002973 /* 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 +01002974 static u32 seeds[NUM_CHANNELS][3] = {
2975 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
2976 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
2977 };
2978 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002979 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002980 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
2981 MCHBAR32(SCRAMBLING_SEED_2_HI_ch(channel)) = seeds[channel][1];
2982 MCHBAR32(SCRAMBLING_SEED_2_LO_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002983 }
2984}
2985
Angel Pons89ae6b82020-03-21 13:23:32 +01002986void set_wmm_behavior(const u32 cpu)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002987{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002988 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002989 MCHBAR32(SC_WDBWM) = 0x141d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002990 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002991 MCHBAR32(SC_WDBWM) = 0x551d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002992 }
2993}
2994
Angel Pons88521882020-01-05 20:21:20 +01002995void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002996{
2997 int channel;
2998
2999 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003000 /* Always drive command bus */
Angel Pons88521882020-01-05 20:21:20 +01003001 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003002 }
3003
3004 udelay(1);
3005
3006 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003007 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003008 }
3009}
3010
Angel Pons7c49cb82020-03-16 23:17:32 +01003011void set_read_write_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003012{
3013 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003014
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003015 FOR_ALL_POPULATED_CHANNELS {
3016 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003017 int min_pi = 10000;
3018 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003019
3020 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003021 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3022 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003023 }
3024
Angel Pons7c49cb82020-03-16 23:17:32 +01003025 b20 = (max_pi - min_pi > 51) ? 0 : ctrl->ref_card_offset[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003026
Angel Pons7c49cb82020-03-16 23:17:32 +01003027 b4_8_12 = (ctrl->pi_coding_threshold < max_pi - min_pi) ? 0x3330 : 0x2220;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003028
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003029 dram_odt_stretch(ctrl, channel);
3030
Angel Pons7c49cb82020-03-16 23:17:32 +01003031 MCHBAR32(TC_RWP_ch(channel)) = 0x0a000000 | (b20 << 20) |
Felix Held2463aa92018-07-29 21:37:55 +02003032 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003033 }
3034}
3035
Angel Pons88521882020-01-05 20:21:20 +01003036void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003037{
3038 int channel;
3039 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003040 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3041 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003042 }
3043}
3044
Angel Pons7c49cb82020-03-16 23:17:32 +01003045/* Encode the watermark latencies in a suitable format for graphics drivers consumption */
3046static int encode_wm(int ns)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003047{
Angel Pons88521882020-01-05 20:21:20 +01003048 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003049}
3050
Angel Pons7c49cb82020-03-16 23:17:32 +01003051/* FIXME: values in this function should be hardware revision-dependent */
Angel Pons88521882020-01-05 20:21:20 +01003052void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003053{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003054 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3055
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003056 int channel;
3057 int t1_cycles = 0, t1_ns = 0, t2_ns;
3058 int t3_ns;
3059 u32 r32;
3060
Angel Pons7c49cb82020-03-16 23:17:32 +01003061 /* FIXME: This register only exists on Ivy Bridge */
3062 MCHBAR32(WMM_READ_CONFIG) = 0x46;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003063
Felix Heldf9b826a2018-07-30 17:56:52 +02003064 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003065 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xffffcfff, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003066
Patrick Rudolph74203de2017-11-20 11:57:01 +01003067 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003068 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003069 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003070 else
Angel Pons7c49cb82020-03-16 23:17:32 +01003071 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003072 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003073
Felix Heldf9b826a2018-07-30 17:56:52 +02003074 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003075 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003076
Angel Pons88521882020-01-05 20:21:20 +01003077 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3078 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003079
3080 FOR_ALL_CHANNELS {
3081 switch (ctrl->rankmap[channel]) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003082 /* Unpopulated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003083 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003084 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003085 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003086 /* Only single-ranked dimms */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003087 case 1:
3088 case 4:
3089 case 5:
Angel Pons7c49cb82020-03-16 23:17:32 +01003090 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x00373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003091 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003092 /* Dual-ranked dimms present */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003093 default:
Angel Pons7c49cb82020-03-16 23:17:32 +01003094 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x009b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003095 break;
3096 }
3097 }
3098
Felix Held50b7ed22019-12-30 20:41:54 +01003099 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
Angel Pons7c49cb82020-03-16 23:17:32 +01003100 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0x00ffffff, 0x00e4d5d0);
Felix Held50b7ed22019-12-30 20:41:54 +01003101 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003102
3103 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003104 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~(3 << 16), 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003105
Angel Pons88521882020-01-05 20:21:20 +01003106 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3107 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3108 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003109
Angel Pons7c49cb82020-03-16 23:17:32 +01003110 /* Find a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003111 FOR_ALL_POPULATED_CHANNELS
3112 break;
3113
Angel Pons88521882020-01-05 20:21:20 +01003114 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3115 r32 = MCHBAR32(PM_DLL_CONFIG);
Angel Pons7c49cb82020-03-16 23:17:32 +01003116 if (r32 & (1 << 17))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003117 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003118 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003119 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
Angel Pons7c49cb82020-03-16 23:17:32 +01003120 if (!(r32 & (1 << 17)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003121 t1_ns += 500;
3122
Angel Pons88521882020-01-05 20:21:20 +01003123 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003124 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003125 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003126 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003127 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003128 t3_ns = 500;
3129 }
Angel Pons7c49cb82020-03-16 23:17:32 +01003130
3131 /* The graphics driver will use these watermark values */
3132 printk(BIOS_DEBUG, "t123: %d, %d, %d\n", t1_ns, t2_ns, t3_ns);
3133 MCHBAR32_AND_OR(SSKPD, 0xC0C0C0C0,
3134 ((encode_wm(t1_ns) + encode_wm(t2_ns)) << 16) | (encode_wm(t1_ns) << 8) |
3135 ((encode_wm(t3_ns) + encode_wm(t2_ns) + encode_wm(t1_ns)) << 24) | 0x0c);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003136}
3137
Angel Pons88521882020-01-05 20:21:20 +01003138void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003139{
3140 int channel, slotrank, lane;
3141
Angel Pons7c49cb82020-03-16 23:17:32 +01003142 FOR_ALL_POPULATED_CHANNELS {
3143 MCHBAR32(TC_RAP_ch(channel)) =
3144 (ctrl->tRRD << 0)
3145 | (ctrl->tRTP << 4)
3146 | (ctrl->tCKE << 8)
3147 | (ctrl->tWTR << 12)
3148 | (ctrl->tFAW << 16)
3149 | (ctrl->tWR << 24)
3150 | (ctrl->cmd_stretch[channel] << 30);
3151 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003152
3153 udelay(1);
3154
3155 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003156 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003157 }
3158
3159 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003160 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003161 }
3162
3163 FOR_ALL_POPULATED_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003164 MCHBAR32_OR(TC_RWP_ch(channel), 0x08000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003165
3166 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003167 udelay(1);
3168 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x00200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003169 }
3170
3171 printram("CPE\n");
3172
Angel Pons88521882020-01-05 20:21:20 +01003173 MCHBAR32(GDCRTRAININGMOD) = 0;
3174 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003175
3176 printram("CP5b\n");
3177
3178 FOR_ALL_POPULATED_CHANNELS {
3179 program_timings(ctrl, channel);
3180 }
3181
3182 u32 reg, addr;
3183
Angel Pons7c49cb82020-03-16 23:17:32 +01003184 /* Poll for RCOMP */
3185 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
3186 ;
3187
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003188 do {
Angel Pons88521882020-01-05 20:21:20 +01003189 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003190 } while ((reg & 0x14) == 0);
3191
Angel Pons7c49cb82020-03-16 23:17:32 +01003192 /* Set state of memory controller */
Angel Pons88521882020-01-05 20:21:20 +01003193 MCHBAR32(MC_INIT_STATE_G) = 0x116;
Angel Pons7c49cb82020-03-16 23:17:32 +01003194 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003195
Angel Pons7c49cb82020-03-16 23:17:32 +01003196 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003197 udelay(500);
3198
3199 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003200 /* Set valid rank CKE */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003201 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01003202 reg = (reg & ~0x0f) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003203 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003204 MCHBAR32(addr) = reg;
3205
Angel Pons7c49cb82020-03-16 23:17:32 +01003206 /* Wait 10ns for ranks to settle */
3207 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003208
3209 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3210 MCHBAR32(addr) = reg;
3211
Angel Pons7c49cb82020-03-16 23:17:32 +01003212 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003213 write_reset(ctrl);
3214 }
3215
Angel Pons7c49cb82020-03-16 23:17:32 +01003216 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003217 dram_mrscommands(ctrl);
3218
3219 printram("CP5c\n");
3220
Angel Pons88521882020-01-05 20:21:20 +01003221 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003222
3223 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003224 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003225 udelay(2);
3226 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003227}