blob: 2cb6a8337bba724bc435ba01f1323c88478c1836 [file] [log] [blame]
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001/*
2 * This file is part of the coreboot project.
3 *
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
Elyes HAOUASf97c1c92019-12-03 18:22:06 +010015#include <commonlib/helpers.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010016#include <console/console.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010017#include <string.h>
Subrata Banik53b08c32018-12-10 14:11:35 +053018#include <arch/cpu.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +020019#include <device/mmio.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +020020#include <device/pci_ops.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010021#include <northbridge/intel/sandybridge/chip.h>
22#include <device/pci_def.h>
23#include <delay.h>
Elyes HAOUAS1d3b3c32019-05-04 08:12:42 +020024
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010025#include "raminit_native.h"
26#include "raminit_common.h"
27#include "sandybridge.h"
28
Angel Pons7c49cb82020-03-16 23:17:32 +010029/* FIXME: no ECC support */
30/* FIXME: no support for 3-channel chipsets */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010031
Angel Pons88521882020-01-05 20:21:20 +010032/* length: [1..4] */
33#define IOSAV_RUN_ONCE(length) ((((length) - 1) << 18) | 1)
Felix Held9cf1dd22018-07-31 14:52:40 +020034
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010035static void sfence(void)
36{
37 asm volatile ("sfence");
38}
39
Angel Pons7c49cb82020-03-16 23:17:32 +010040/* Toggle IO reset bit */
41static void toggle_io_reset(void)
42{
Angel Pons88521882020-01-05 20:21:20 +010043 u32 r32 = MCHBAR32(MC_INIT_STATE_G);
Angel Pons7c49cb82020-03-16 23:17:32 +010044 MCHBAR32(MC_INIT_STATE_G) = r32 | 0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010045 udelay(1);
Angel Pons88521882020-01-05 20:21:20 +010046 MCHBAR32(MC_INIT_STATE_G) = r32 & ~0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010047 udelay(1);
48}
49
50static u32 get_XOVER_CLK(u8 rankmap)
51{
52 return rankmap << 24;
53}
54
55static u32 get_XOVER_CMD(u8 rankmap)
56{
57 u32 reg;
58
Angel Pons7c49cb82020-03-16 23:17:32 +010059 /* Enable xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010060 reg = 0x4000;
61
Angel Pons7c49cb82020-03-16 23:17:32 +010062 /* Enable xover ctl */
63 if (rankmap & 0x03)
64 reg |= (1 << 17);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010065
Angel Pons7c49cb82020-03-16 23:17:32 +010066 if (rankmap & 0x0c)
67 reg |= (1 << 26);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010068
69 return reg;
70}
71
Angel Pons7c49cb82020-03-16 23:17:32 +010072/* CAS write latency. To be programmed in MR2. See DDR3 SPEC for MR2 documentation. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010073u8 get_CWL(u32 tCK)
74{
Angel Pons7c49cb82020-03-16 23:17:32 +010075 /* Get CWL based on tCK using the following rule */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010076 switch (tCK) {
77 case TCK_1333MHZ:
78 return 12;
Angel Pons7c49cb82020-03-16 23:17:32 +010079
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010080 case TCK_1200MHZ:
81 case TCK_1100MHZ:
82 return 11;
Angel Pons7c49cb82020-03-16 23:17:32 +010083
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010084 case TCK_1066MHZ:
85 case TCK_1000MHZ:
86 return 10;
Angel Pons7c49cb82020-03-16 23:17:32 +010087
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010088 case TCK_933MHZ:
89 case TCK_900MHZ:
90 return 9;
Angel Pons7c49cb82020-03-16 23:17:32 +010091
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010092 case TCK_800MHZ:
93 case TCK_700MHZ:
94 return 8;
Angel Pons7c49cb82020-03-16 23:17:32 +010095
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010096 case TCK_666MHZ:
97 return 7;
Angel Pons7c49cb82020-03-16 23:17:32 +010098
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010099 case TCK_533MHZ:
100 return 6;
Angel Pons7c49cb82020-03-16 23:17:32 +0100101
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100102 default:
103 return 5;
104 }
105}
106
107void dram_find_common_params(ramctr_timing *ctrl)
108{
109 size_t valid_dimms;
110 int channel, slot;
111 dimm_info *dimms = &ctrl->info;
112
113 ctrl->cas_supported = (1 << (MAX_CAS - MIN_CAS + 1)) - 1;
114 valid_dimms = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100115
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100116 FOR_ALL_CHANNELS for (slot = 0; slot < 2; slot++) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100117
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100118 const dimm_attr *dimm = &dimms->dimm[channel][slot];
119 if (dimm->dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3)
120 continue;
Angel Pons7c49cb82020-03-16 23:17:32 +0100121
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100122 valid_dimms++;
123
124 /* Find all possible CAS combinations */
125 ctrl->cas_supported &= dimm->cas_supported;
126
127 /* Find the smallest common latencies supported by all DIMMs */
Angel Pons7c49cb82020-03-16 23:17:32 +0100128 ctrl->tCK = MAX(ctrl->tCK, dimm->tCK);
129 ctrl->tAA = MAX(ctrl->tAA, dimm->tAA);
130 ctrl->tWR = MAX(ctrl->tWR, dimm->tWR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100131 ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD);
132 ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD);
Angel Pons7c49cb82020-03-16 23:17:32 +0100133 ctrl->tRP = MAX(ctrl->tRP, dimm->tRP);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100134 ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS);
135 ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC);
136 ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR);
137 ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP);
138 ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW);
Dan Elkoubydabebc32018-04-13 18:47:10 +0300139 ctrl->tCWL = MAX(ctrl->tCWL, dimm->tCWL);
140 ctrl->tCMD = MAX(ctrl->tCMD, dimm->tCMD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100141 }
142
143 if (!ctrl->cas_supported)
Angel Pons7c49cb82020-03-16 23:17:32 +0100144 die("Unsupported DIMM combination. DIMMS do not support common CAS latency");
145
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100146 if (!valid_dimms)
147 die("No valid DIMMs found");
148}
149
Angel Pons88521882020-01-05 20:21:20 +0100150void dram_xover(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100151{
152 u32 reg;
153 int channel;
154
155 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100156 /* Enable xover clk */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100157 reg = get_XOVER_CLK(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100158 printram("XOVER CLK [%x] = %x\n", GDCRCKPICODE_ch(channel), reg);
159 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100160
Angel Pons7c49cb82020-03-16 23:17:32 +0100161 /* Enable xover ctl & xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100162 reg = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100163 printram("XOVER CMD [%x] = %x\n", GDCRCMDPICODING_ch(channel), reg);
164 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100165 }
166}
167
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100168static void dram_odt_stretch(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100169{
Iru Cai89af71c2018-08-16 16:46:27 +0800170 u32 addr, cpu, stretch;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100171
172 stretch = ctrl->ref_card_offset[channel];
Angel Pons7c49cb82020-03-16 23:17:32 +0100173 /*
174 * ODT stretch:
175 * Delay ODT signal by stretch value. Useful for multi DIMM setups on the same channel.
176 */
Subrata Banik53b08c32018-12-10 14:11:35 +0530177 cpu = cpu_get_cpuid();
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100178 if (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_C(cpu)) {
179 if (stretch == 2)
180 stretch = 3;
Angel Pons7c49cb82020-03-16 23:17:32 +0100181
Angel Pons88521882020-01-05 20:21:20 +0100182 addr = SCHED_SECOND_CBIT_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100183 MCHBAR32_AND_OR(addr, 0xffffc3ff, (stretch << 12) | (stretch << 10));
184 printk(RAM_DEBUG, "OTHP Workaround [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100185 } else {
Angel Pons88521882020-01-05 20:21:20 +0100186 addr = TC_OTHP_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100187 MCHBAR32_AND_OR(addr, 0xfff0ffff, (stretch << 16) | (stretch << 18));
Iru Cai89af71c2018-08-16 16:46:27 +0800188 printk(RAM_DEBUG, "OTHP [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100189 }
190}
191
192void dram_timing_regs(ramctr_timing *ctrl)
193{
194 u32 reg, addr, val32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100195 int channel;
196
197 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100198 /* BIN parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100199 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100200 reg |= (ctrl->tRCD << 0);
201 reg |= (ctrl->tRP << 4);
202 reg |= (ctrl->CAS << 8);
203 reg |= (ctrl->CWL << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100204 reg |= (ctrl->tRAS << 16);
Angel Pons88521882020-01-05 20:21:20 +0100205 printram("DBP [%x] = %x\n", TC_DBP_ch(channel), reg);
206 MCHBAR32(TC_DBP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100207
Angel Pons7c49cb82020-03-16 23:17:32 +0100208 /* Regular access parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100209 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100210 reg |= (ctrl->tRRD << 0);
211 reg |= (ctrl->tRTP << 4);
212 reg |= (ctrl->tCKE << 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100213 reg |= (ctrl->tWTR << 12);
214 reg |= (ctrl->tFAW << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +0100215 reg |= (ctrl->tWR << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100216 reg |= (3 << 30);
Angel Pons88521882020-01-05 20:21:20 +0100217 printram("RAP [%x] = %x\n", TC_RAP_ch(channel), reg);
218 MCHBAR32(TC_RAP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100219
Angel Pons7c49cb82020-03-16 23:17:32 +0100220 /* Other parameters */
Angel Pons88521882020-01-05 20:21:20 +0100221 addr = TC_OTHP_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100222 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100223 reg |= (ctrl->tXPDLL << 0);
224 reg |= (ctrl->tXP << 5);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100225 reg |= (ctrl->tAONPD << 8);
226 reg |= 0xa0000;
227 printram("OTHP [%x] = %x\n", addr, reg);
228 MCHBAR32(addr) = reg;
229
Angel Pons7c49cb82020-03-16 23:17:32 +0100230 /* FIXME: This register might as well not exist */
Angel Pons1aba2a32020-01-05 22:31:41 +0100231 MCHBAR32(0x4014 + channel * 0x400) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100232
Felix Held9fe248f2018-07-31 20:59:45 +0200233 MCHBAR32_OR(addr, 0x00020000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100234
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100235 dram_odt_stretch(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100236
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100237 /*
Angel Pons7c49cb82020-03-16 23:17:32 +0100238 * TC-Refresh timing parameters:
239 * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow
240 * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024.
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100241 */
242 val32 = MIN((ctrl->tREFI * 89) / 10, (70000 << 8) / ctrl->tCK);
243
Angel Pons7c49cb82020-03-16 23:17:32 +0100244 reg = ((ctrl->tREFI & 0xffff) << 0) |
245 ((ctrl->tRFC & 0x01ff) << 16) | (((val32 / 1024) & 0x7f) << 25);
246
Angel Pons88521882020-01-05 20:21:20 +0100247 printram("REFI [%x] = %x\n", TC_RFTP_ch(channel), reg);
248 MCHBAR32(TC_RFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100249
Angel Pons88521882020-01-05 20:21:20 +0100250 MCHBAR32_OR(TC_RFP_ch(channel), 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100251
Angel Pons7c49cb82020-03-16 23:17:32 +0100252 /* Self-refresh timing parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100253 reg = 0;
254 val32 = tDLLK;
Angel Pons7c49cb82020-03-16 23:17:32 +0100255 reg = (reg & ~0x00000fff) | (val32 << 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100256 val32 = ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100257 reg = (reg & ~0x0000f000) | (val32 << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100258 val32 = tDLLK - ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100259 reg = (reg & ~0x03ff0000) | (val32 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100260 val32 = ctrl->tMOD - 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100261 reg = (reg & ~0xf0000000) | (val32 << 28);
262 printram("SRFTP [%x] = %x\n", TC_SRFTP_ch(channel), reg);
Angel Pons88521882020-01-05 20:21:20 +0100263 MCHBAR32(TC_SRFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100264 }
265}
266
267void dram_dimm_mapping(ramctr_timing *ctrl)
268{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100269 int channel;
270 dimm_info *info = &ctrl->info;
271
272 FOR_ALL_CHANNELS {
Nico Huberac4f2162017-10-01 18:14:43 +0200273 dimm_attr *dimmA, *dimmB;
274 u32 reg = 0;
275
Angel Pons7c49cb82020-03-16 23:17:32 +0100276 if (info->dimm[channel][0].size_mb >= info->dimm[channel][1].size_mb) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100277 dimmA = &info->dimm[channel][0];
278 dimmB = &info->dimm[channel][1];
Angel Pons7c49cb82020-03-16 23:17:32 +0100279 reg |= (0 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100280 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100281 dimmA = &info->dimm[channel][1];
282 dimmB = &info->dimm[channel][0];
Angel Pons7c49cb82020-03-16 23:17:32 +0100283 reg |= (1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100284 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100285
Nico Huberac4f2162017-10-01 18:14:43 +0200286 if (dimmA && (dimmA->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100287 reg |= (dimmA->size_mb / 256) << 0;
288 reg |= (dimmA->ranks - 1) << 17;
Nico Huberac4f2162017-10-01 18:14:43 +0200289 reg |= (dimmA->width / 8 - 1) << 19;
290 }
291
292 if (dimmB && (dimmB->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100293 reg |= (dimmB->size_mb / 256) << 8;
294 reg |= (dimmB->ranks - 1) << 18;
Nico Huberac4f2162017-10-01 18:14:43 +0200295 reg |= (dimmB->width / 8 - 1) << 20;
296 }
297
Angel Pons7c49cb82020-03-16 23:17:32 +0100298 reg |= 1 << 21; /* Rank interleave */
299 reg |= 1 << 22; /* Enhanced interleave */
Nico Huberac4f2162017-10-01 18:14:43 +0200300
Angel Pons7c49cb82020-03-16 23:17:32 +0100301 if ((dimmA && (dimmA->ranks > 0)) || (dimmB && (dimmB->ranks > 0))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100302 ctrl->mad_dimm[channel] = reg;
303 } else {
304 ctrl->mad_dimm[channel] = 0;
305 }
306 }
307}
308
Angel Pons88521882020-01-05 20:21:20 +0100309void dram_dimm_set_mapping(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100310{
311 int channel;
312 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100313 MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100314 }
315}
316
Angel Pons88521882020-01-05 20:21:20 +0100317void dram_zones(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100318{
319 u32 reg, ch0size, ch1size;
320 u8 val;
321 reg = 0;
322 val = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100323
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100324 if (training) {
325 ch0size = ctrl->channel_size_mb[0] ? 256 : 0;
326 ch1size = ctrl->channel_size_mb[1] ? 256 : 0;
327 } else {
328 ch0size = ctrl->channel_size_mb[0];
329 ch1size = ctrl->channel_size_mb[1];
330 }
331
332 if (ch0size >= ch1size) {
Angel Pons88521882020-01-05 20:21:20 +0100333 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100334 val = ch1size / 256;
335 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100336 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100337 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100338 MCHBAR32(MAD_CHNL) = 0x24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100339
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100340 } else {
Angel Pons88521882020-01-05 20:21:20 +0100341 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100342 val = ch0size / 256;
343 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100344 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100345 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100346 MCHBAR32(MAD_CHNL) = 0x21;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100347 }
348}
349
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100350#define DEFAULT_TCK TCK_800MHZ
351
352unsigned int get_mem_min_tck(void)
353{
354 u32 reg32;
355 u8 rev;
356 const struct device *dev;
357 const struct northbridge_intel_sandybridge_config *cfg = NULL;
358
Angel Ponsb31d1d72020-01-10 01:35:09 +0100359 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100360 if (dev)
361 cfg = dev->chip_info;
362
363 /* If this is zero, it just means devicetree.cb didn't set it */
364 if (!cfg || cfg->max_mem_clock_mhz == 0) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100365
Julius Wernercd49cce2019-03-05 16:53:33 -0800366 if (CONFIG(NATIVE_RAMINIT_IGNORE_MAX_MEM_FUSES))
Patrick Rudolphb794a692017-08-08 13:13:51 +0200367 return TCK_1333MHZ;
368
Angel Ponsb31d1d72020-01-10 01:35:09 +0100369 rev = pci_read_config8(HOST_BRIDGE, PCI_DEVICE_ID);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100370
371 if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100372 /* Read Capabilities A Register DMFC bits */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100373 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100374 reg32 &= 0x7;
375
376 switch (reg32) {
377 case 7: return TCK_533MHZ;
378 case 6: return TCK_666MHZ;
379 case 5: return TCK_800MHZ;
Angel Pons7c49cb82020-03-16 23:17:32 +0100380 /* Reserved */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100381 default:
382 break;
383 }
384 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +0100385 /* Read Capabilities B Register DMFC bits */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100386 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_B);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100387 reg32 = (reg32 >> 4) & 0x7;
388
389 switch (reg32) {
390 case 7: return TCK_533MHZ;
391 case 6: return TCK_666MHZ;
392 case 5: return TCK_800MHZ;
393 case 4: return TCK_933MHZ;
394 case 3: return TCK_1066MHZ;
395 case 2: return TCK_1200MHZ;
396 case 1: return TCK_1333MHZ;
Angel Pons7c49cb82020-03-16 23:17:32 +0100397 /* Reserved */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100398 default:
399 break;
400 }
401 }
402 return DEFAULT_TCK;
403 } else {
404 if (cfg->max_mem_clock_mhz >= 1066)
405 return TCK_1066MHZ;
406 else if (cfg->max_mem_clock_mhz >= 933)
407 return TCK_933MHZ;
408 else if (cfg->max_mem_clock_mhz >= 800)
409 return TCK_800MHZ;
410 else if (cfg->max_mem_clock_mhz >= 666)
411 return TCK_666MHZ;
412 else if (cfg->max_mem_clock_mhz >= 533)
413 return TCK_533MHZ;
414 else
415 return TCK_400MHZ;
416 }
417}
418
419#define DEFAULT_PCI_MMIO_SIZE 2048
420
421static unsigned int get_mmio_size(void)
422{
423 const struct device *dev;
424 const struct northbridge_intel_sandybridge_config *cfg = NULL;
425
Angel Ponsb31d1d72020-01-10 01:35:09 +0100426 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100427 if (dev)
428 cfg = dev->chip_info;
429
430 /* If this is zero, it just means devicetree.cb didn't set it */
431 if (!cfg || cfg->pci_mmio_size == 0)
432 return DEFAULT_PCI_MMIO_SIZE;
433 else
434 return cfg->pci_mmio_size;
435}
436
Angel Pons88521882020-01-05 20:21:20 +0100437void dram_memorymap(ramctr_timing *ctrl, int me_uma_size)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100438{
Angel Pons7c49cb82020-03-16 23:17:32 +0100439 u32 reg, val, reclaim, tom, gfxstolen, gttsize;
440 size_t tsegbase, toludbase, remapbase, gfxstolenbase, mmiosize, gttbase;
441 size_t tsegsize, touudbase, remaplimit, mestolenbase, tsegbasedelta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100442 uint16_t ggc;
443
444 mmiosize = get_mmio_size();
445
Felix Held87ddea22020-01-26 04:55:27 +0100446 ggc = pci_read_config16(HOST_BRIDGE, GGC);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100447 if (!(ggc & 2)) {
448 gfxstolen = ((ggc >> 3) & 0x1f) * 32;
Angel Pons7c49cb82020-03-16 23:17:32 +0100449 gttsize = ((ggc >> 8) & 0x3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100450 } else {
451 gfxstolen = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100452 gttsize = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100453 }
454
455 tsegsize = CONFIG_SMM_TSEG_SIZE >> 20;
456
457 tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1];
458
459 mestolenbase = tom - me_uma_size;
460
Angel Pons7c49cb82020-03-16 23:17:32 +0100461 toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize, tom - me_uma_size);
462
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100463 gfxstolenbase = toludbase - gfxstolen;
464 gttbase = gfxstolenbase - gttsize;
465
466 tsegbase = gttbase - tsegsize;
467
Angel Pons7c49cb82020-03-16 23:17:32 +0100468 /* Round tsegbase down to nearest address aligned to tsegsize */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100469 tsegbasedelta = tsegbase & (tsegsize - 1);
470 tsegbase &= ~(tsegsize - 1);
471
472 gttbase -= tsegbasedelta;
473 gfxstolenbase -= tsegbasedelta;
474 toludbase -= tsegbasedelta;
475
Angel Pons7c49cb82020-03-16 23:17:32 +0100476 /* Test if it is possible to reclaim a hole in the RAM addressing */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100477 if (tom - me_uma_size > toludbase) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100478 /* Reclaim is possible */
479 reclaim = 1;
480 remapbase = MAX(4096, tom - me_uma_size);
481 remaplimit = remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1;
482 touudbase = remaplimit + 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100483 } else {
484 // Reclaim not possible
Angel Pons7c49cb82020-03-16 23:17:32 +0100485 reclaim = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100486 touudbase = tom - me_uma_size;
487 }
488
Angel Pons7c49cb82020-03-16 23:17:32 +0100489 /* Update memory map in PCIe configuration space */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100490 printk(BIOS_DEBUG, "Update PCI-E configuration space:\n");
491
Angel Pons7c49cb82020-03-16 23:17:32 +0100492 /* TOM (top of memory) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100493 reg = pci_read_config32(HOST_BRIDGE, TOM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100494 val = tom & 0xfff;
495 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100496 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100497 pci_write_config32(HOST_BRIDGE, TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100498
Angel Ponsb31d1d72020-01-10 01:35:09 +0100499 reg = pci_read_config32(HOST_BRIDGE, TOM + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100500 val = tom & 0xfffff000;
501 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100502 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100503 pci_write_config32(HOST_BRIDGE, TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100504
Angel Pons7c49cb82020-03-16 23:17:32 +0100505 /* TOLUD (Top Of Low Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100506 reg = pci_read_config32(HOST_BRIDGE, TOLUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100507 val = toludbase & 0xfff;
508 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100509 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOLUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100510 pci_write_config32(HOST_BRIDGE, TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100511
Angel Pons7c49cb82020-03-16 23:17:32 +0100512 /* TOUUD LSB (Top Of Upper Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100513 reg = pci_read_config32(HOST_BRIDGE, TOUUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100514 val = touudbase & 0xfff;
515 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100516 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100517 pci_write_config32(HOST_BRIDGE, TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100518
Angel Pons7c49cb82020-03-16 23:17:32 +0100519 /* TOUUD MSB */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100520 reg = pci_read_config32(HOST_BRIDGE, TOUUD + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100521 val = touudbase & 0xfffff000;
522 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100523 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100524 pci_write_config32(HOST_BRIDGE, TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100525
526 if (reclaim) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100527 /* REMAP BASE */
528 pci_write_config32(HOST_BRIDGE, REMAPBASE, remapbase << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100529 pci_write_config32(HOST_BRIDGE, REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100530
Angel Pons7c49cb82020-03-16 23:17:32 +0100531 /* REMAP LIMIT */
532 pci_write_config32(HOST_BRIDGE, REMAPLIMIT, remaplimit << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100533 pci_write_config32(HOST_BRIDGE, REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100534 }
Angel Pons7c49cb82020-03-16 23:17:32 +0100535 /* TSEG */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100536 reg = pci_read_config32(HOST_BRIDGE, TSEGMB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100537 val = tsegbase & 0xfff;
538 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100539 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TSEGMB, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100540 pci_write_config32(HOST_BRIDGE, TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100541
Angel Pons7c49cb82020-03-16 23:17:32 +0100542 /* GFX stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100543 reg = pci_read_config32(HOST_BRIDGE, BDSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100544 val = gfxstolenbase & 0xfff;
545 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100546 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BDSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100547 pci_write_config32(HOST_BRIDGE, BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100548
Angel Pons7c49cb82020-03-16 23:17:32 +0100549 /* GTT stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100550 reg = pci_read_config32(HOST_BRIDGE, BGSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100551 val = gttbase & 0xfff;
552 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100553 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BGSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100554 pci_write_config32(HOST_BRIDGE, BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100555
556 if (me_uma_size) {
Angel Ponsb31d1d72020-01-10 01:35:09 +0100557 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100558 val = (0x80000 - me_uma_size) & 0xfffff000;
559 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100560 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100561 pci_write_config32(HOST_BRIDGE, MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100562
Angel Pons7c49cb82020-03-16 23:17:32 +0100563 /* ME base */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100564 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100565 val = mestolenbase & 0xfff;
566 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held651f99f2019-12-30 16:28:48 +0100567 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100568 pci_write_config32(HOST_BRIDGE, MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100569
Angel Ponsb31d1d72020-01-10 01:35:09 +0100570 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100571 val = mestolenbase & 0xfffff000;
572 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100573 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100574 pci_write_config32(HOST_BRIDGE, MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100575
Angel Pons7c49cb82020-03-16 23:17:32 +0100576 /* ME mask */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100577 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100578 val = (0x80000 - me_uma_size) & 0xfff;
579 reg = (reg & ~0xfff00000) | (val << 20);
Angel Pons7c49cb82020-03-16 23:17:32 +0100580 reg = reg | ME_STLEN_EN; /* Set ME memory enable */
581 reg = reg | MELCK; /* Set lock bit on ME mem */
Felix Held651f99f2019-12-30 16:28:48 +0100582 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100583 pci_write_config32(HOST_BRIDGE, MESEG_MASK, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100584 }
585}
586
Angel Pons88521882020-01-05 20:21:20 +0100587static void wait_for_iosav(int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100588{
589 while (1) {
Angel Pons88521882020-01-05 20:21:20 +0100590 if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100591 return;
592 }
593}
594
Angel Pons88521882020-01-05 20:21:20 +0100595static void write_reset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100596{
597 int channel, slotrank;
598
Angel Pons7c49cb82020-03-16 23:17:32 +0100599 /* Choose a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100600 channel = (ctrl->rankmap[0]) ? 0 : 1;
601
Angel Pons88521882020-01-05 20:21:20 +0100602 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100603
Angel Pons7c49cb82020-03-16 23:17:32 +0100604 /* Choose a populated rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100605 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
606
607 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100608 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
609 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x80c01;
Angel Pons88521882020-01-05 20:21:20 +0100610 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100611 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100612
Angel Pons7c49cb82020-03-16 23:17:32 +0100613 /*
614 * Execute command queue - why is bit 22 set here?!
615 *
616 * This is actually using the IOSAV state machine as a timer, so refresh is allowed.
617 */
Angel Pons88521882020-01-05 20:21:20 +0100618 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = (1 << 22) | IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +0200619
Angel Pons88521882020-01-05 20:21:20 +0100620 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100621}
622
Angel Pons88521882020-01-05 20:21:20 +0100623void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100624{
Felix Held9fe248f2018-07-31 20:59:45 +0200625 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100626 int channel;
627
Angel Pons7c49cb82020-03-16 23:17:32 +0100628 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
629 ;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100630 do {
Angel Pons88521882020-01-05 20:21:20 +0100631 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100632 } while ((reg & 0x14) == 0);
633
Angel Pons7c49cb82020-03-16 23:17:32 +0100634 /* Set state of memory controller */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100635 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100636 MCHBAR32(MC_INIT_STATE_G) = reg;
637 MCHBAR32(MC_INIT_STATE) = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100638 reg |= 2; /* DDR reset */
Angel Pons88521882020-01-05 20:21:20 +0100639 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100640
Angel Pons7c49cb82020-03-16 23:17:32 +0100641 /* Assert DIMM reset signal */
642 MCHBAR32_AND(MC_INIT_STATE_G, ~2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100643
Angel Pons7c49cb82020-03-16 23:17:32 +0100644 /* Wait 200us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100645 udelay(200);
646
Angel Pons7c49cb82020-03-16 23:17:32 +0100647 /* Deassert DIMM reset signal */
Angel Pons88521882020-01-05 20:21:20 +0100648 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100649
Angel Pons7c49cb82020-03-16 23:17:32 +0100650 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100651 udelay(500);
652
Angel Pons7c49cb82020-03-16 23:17:32 +0100653 /* Enable DCLK */
Angel Pons88521882020-01-05 20:21:20 +0100654 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100655
Angel Pons7c49cb82020-03-16 23:17:32 +0100656 /* XXX Wait 20ns */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100657 udelay(1);
658
659 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100660 /* Set valid rank CKE */
Felix Held9fe248f2018-07-31 20:59:45 +0200661 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100662 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100663
Angel Pons7c49cb82020-03-16 23:17:32 +0100664 /* Wait 10ns for ranks to settle */
665 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100666
667 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100668 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100669
Angel Pons7c49cb82020-03-16 23:17:32 +0100670 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100671 write_reset(ctrl);
672 }
673}
674
675static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
676{
Angel Pons7c49cb82020-03-16 23:17:32 +0100677 /* Get ODT based on rankmap */
678 int dimms_per_ch = (ctrl->rankmap[channel] & 1) + ((ctrl->rankmap[channel] >> 2) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100679
680 if (dimms_per_ch == 1) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100681 return (const odtmap){60, 60};
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100682 } else {
683 return (const odtmap){120, 30};
684 }
685}
686
Angel Pons7c49cb82020-03-16 23:17:32 +0100687static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank, int reg, u32 val)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100688{
Angel Pons88521882020-01-05 20:21:20 +0100689 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100690
691 if (ctrl->rank_mirror[channel][slotrank]) {
692 /* DDR3 Rank1 Address mirror
Angel Pons7c49cb82020-03-16 23:17:32 +0100693 swap the following pins:
694 A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100695 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
Angel Pons7c49cb82020-03-16 23:17:32 +0100696 val = (val & ~0x1f8) | ((val >> 1) & 0xa8) | ((val & 0xa8) << 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100697 }
698
699 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100700 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f000;
701 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100702 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200703 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100704 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100705
706 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100707 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f000;
708 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100709 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200710 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100711 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100712
713 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100714 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x0f000;
715 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +0100716 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200717 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100718 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200719
Angel Pons7c49cb82020-03-16 23:17:32 +0100720 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100721 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100722}
723
Angel Pons88521882020-01-05 20:21:20 +0100724static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100725{
726 u16 mr0reg, mch_cas, mch_wr;
727 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 +0100728 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100729
730 /* DLL Reset - self clearing - set after CLK frequency has been changed */
731 mr0reg = 0x100;
732
Angel Pons7c49cb82020-03-16 23:17:32 +0100733 /* Convert CAS to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100734 if (ctrl->CAS < 12) {
735 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
736 } else {
737 mch_cas = (u16) (ctrl->CAS - 12);
738 mch_cas = ((mch_cas << 1) | 0x1);
739 }
740
Angel Pons7c49cb82020-03-16 23:17:32 +0100741 /* Convert tWR to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100742 mch_wr = mch_wr_t[ctrl->tWR - 5];
743
Angel Pons7c49cb82020-03-16 23:17:32 +0100744 mr0reg = (mr0reg & ~0x0004) | ((mch_cas & 0x1) << 2);
745 mr0reg = (mr0reg & ~0x0070) | ((mch_cas & 0xe) << 3);
746 mr0reg = (mr0reg & ~0x0e00) | (mch_wr << 9);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100747
Angel Pons7c49cb82020-03-16 23:17:32 +0100748 /* Precharge PD - Fast (desktop) 1 or slow (mobile) 0 - mostly power-saving feature */
749 mr0reg = (mr0reg & ~(1 << 12)) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100750 return mr0reg;
751}
752
753static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
754{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200755 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100756}
757
758static u32 encode_odt(u32 odt)
759{
760 switch (odt) {
761 case 30:
762 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
763 case 60:
764 return (1 << 2); // RZQ/4
765 case 120:
766 return (1 << 6); // RZQ/2
767 default:
768 case 0:
769 return 0;
770 }
771}
772
773static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
774{
775 odtmap odt;
776 u32 mr1reg;
777
778 odt = get_ODT(ctrl, rank, channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100779 mr1reg = 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100780
781 mr1reg |= encode_odt(odt.rttnom);
782
783 return mr1reg;
784}
785
786static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
787{
788 u16 mr1reg;
789
790 mr1reg = make_mr1(ctrl, rank, channel);
791
792 write_mrreg(ctrl, channel, rank, 1, mr1reg);
793}
794
795static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
796{
797 u16 pasr, cwl, mr2reg;
798 odtmap odt;
799 int srt;
800
801 pasr = 0;
802 cwl = ctrl->CWL - 5;
803 odt = get_ODT(ctrl, rank, channel);
804
805 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
806
807 mr2reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100808 mr2reg = (mr2reg & ~0x07) | pasr;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100809 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
810 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
811 mr2reg = (mr2reg & ~0x80) | (srt << 7);
812 mr2reg |= (odt.rttwr / 60) << 9;
813
814 write_mrreg(ctrl, channel, rank, 2, mr2reg);
815}
816
817static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
818{
819 write_mrreg(ctrl, channel, rank, 3, 0);
820}
821
Angel Pons88521882020-01-05 20:21:20 +0100822void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100823{
824 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100825 int channel;
826
827 FOR_ALL_POPULATED_CHANNELS {
828 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100829 /* MR2 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100830 dram_mr2(ctrl, slotrank, channel);
831
Angel Pons7c49cb82020-03-16 23:17:32 +0100832 /* MR3 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100833 dram_mr3(ctrl, slotrank, channel);
834
Angel Pons7c49cb82020-03-16 23:17:32 +0100835 /* MR1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100836 dram_mr1(ctrl, slotrank, channel);
837
Angel Pons7c49cb82020-03-16 23:17:32 +0100838 /* MR0 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100839 dram_mr0(ctrl, slotrank, channel);
840 }
841 }
842
843 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +0100844 MCHBAR32(IOSAV_n_SP_CMD_CTRL(0)) = 0x7;
845 MCHBAR32(IOSAV_n_SUBSEQ_CTRL(0)) = 0xf1001;
Angel Pons88521882020-01-05 20:21:20 +0100846 MCHBAR32(IOSAV_n_SP_CMD_ADDR(0)) = 0x60002;
Angel Pons7c49cb82020-03-16 23:17:32 +0100847 MCHBAR32(IOSAV_n_ADDR_UPDATE(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100848
849 /* DRAM command ZQCL */
Angel Pons7c49cb82020-03-16 23:17:32 +0100850 MCHBAR32(IOSAV_n_SP_CMD_CTRL(1)) = 0x1f003;
851 MCHBAR32(IOSAV_n_SUBSEQ_CTRL(1)) = 0x1901001;
Angel Pons88521882020-01-05 20:21:20 +0100852 MCHBAR32(IOSAV_n_SP_CMD_ADDR(1)) = 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +0100853 MCHBAR32(IOSAV_n_ADDR_UPDATE(1)) = 0x288;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100854
Angel Pons7c49cb82020-03-16 23:17:32 +0100855 /* Execute command queue on all channels. Do it four times. */
856 MCHBAR32(IOSAV_SEQ_CTL) = (1 << 18) | 4;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100857
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100858 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100859 /* Wait for ref drained */
Angel Pons88521882020-01-05 20:21:20 +0100860 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100861 }
862
Angel Pons7c49cb82020-03-16 23:17:32 +0100863 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +0100864 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100865
866 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +0100867 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100868
Angel Pons88521882020-01-05 20:21:20 +0100869 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100870
871 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
872
Angel Pons7c49cb82020-03-16 23:17:32 +0100873 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100874 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100875
876 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100877 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
878 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
879 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
880 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200881
Angel Pons7c49cb82020-03-16 23:17:32 +0100882 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100883 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100884
Angel Pons7c49cb82020-03-16 23:17:32 +0100885 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100886 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100887 }
888}
889
Felix Held3b906032020-01-14 17:05:43 +0100890static const u32 lane_base[] = {
891 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
892 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
893 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100894};
895
Angel Pons88521882020-01-05 20:21:20 +0100896void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100897{
Angel Pons88521882020-01-05 20:21:20 +0100898 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100899 int lane;
900 int slotrank, slot;
901 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +0100902 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100903
904 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +0100905 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
906 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100907 }
908
909 for (slot = 0; slot < NUM_SLOTS; slot++)
910 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
911 case 0:
912 default:
Angel Pons88521882020-01-05 20:21:20 +0100913 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100914 break;
915 case 1:
Angel Pons88521882020-01-05 20:21:20 +0100916 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100917 ctrl->timings[channel][2 * slot + 0].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100918 break;
919 case 2:
Angel Pons88521882020-01-05 20:21:20 +0100920 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100921 ctrl->timings[channel][2 * slot + 1].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100922 break;
923 case 3:
Angel Pons88521882020-01-05 20:21:20 +0100924 pi_coding_ctrl[slot] =
925 (ctrl->timings[channel][2 * slot].pi_coding +
Angel Pons7c49cb82020-03-16 23:17:32 +0100926 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100927 break;
928 }
929
Angel Pons7c49cb82020-03-16 23:17:32 +0100930 /* Enable CMD XOVER */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100931 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons7c49cb82020-03-16 23:17:32 +0100932 reg32 |= (pi_coding_ctrl[0] & 0x3f) << 6;
933 reg32 |= (pi_coding_ctrl[0] & 0x40) << 9;
Angel Pons88521882020-01-05 20:21:20 +0100934 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100935 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
936
Angel Pons88521882020-01-05 20:21:20 +0100937 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100938
Angel Pons7c49cb82020-03-16 23:17:32 +0100939 /* Enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +0100940 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
941 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100942
943 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100944 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Angel Pons88521882020-01-05 20:21:20 +0100945 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100946 if (shift < 0)
947 shift = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100948
Angel Pons88521882020-01-05 20:21:20 +0100949 offset_pi_code = ctrl->pi_code_offset + shift;
Angel Pons7c49cb82020-03-16 23:17:32 +0100950
951 /* Set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +0100952 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
953 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100954 }
955
Angel Pons88521882020-01-05 20:21:20 +0100956 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
957 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100958
Angel Pons88521882020-01-05 20:21:20 +0100959 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +0100960 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100961
Angel Pons88521882020-01-05 20:21:20 +0100962 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100963
964 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100965 int post_timA_min_high = 7, pre_timA_min_high = 7;
966 int post_timA_max_high = 0, pre_timA_max_high = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100967 int shift_402x = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100968 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100969
970 if (shift < 0)
971 shift = 0;
972
973 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +0200974 post_timA_min_high = MIN(post_timA_min_high,
975 (ctrl->timings[channel][slotrank].lanes[lane].
976 timA + shift) >> 6);
977 pre_timA_min_high = MIN(pre_timA_min_high,
978 ctrl->timings[channel][slotrank].lanes[lane].
979 timA >> 6);
980 post_timA_max_high = MAX(post_timA_max_high,
981 (ctrl->timings[channel][slotrank].lanes[lane].
982 timA + shift) >> 6);
983 pre_timA_max_high = MAX(pre_timA_max_high,
984 ctrl->timings[channel][slotrank].lanes[lane].
985 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100986 }
987
988 if (pre_timA_max_high - pre_timA_min_high <
989 post_timA_max_high - post_timA_min_high)
990 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +0100991
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100992 else if (pre_timA_max_high - pre_timA_min_high >
993 post_timA_max_high - post_timA_min_high)
994 shift_402x = -1;
995
Felix Helddee167e2019-12-30 17:30:16 +0100996 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +0100997 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100998 post_timA_min_high) << (4 * slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +0100999
Angel Pons88521882020-01-05 20:21:20 +01001000 reg_roundtrip_latency |=
1001 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001002 shift_402x) << (8 * slotrank);
1003
1004 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001005 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001006 (((ctrl->timings[channel][slotrank].lanes[lane].
1007 timA + shift) & 0x3f)
1008 |
1009 ((ctrl->timings[channel][slotrank].lanes[lane].
1010 rising + shift) << 8)
1011 |
1012 (((ctrl->timings[channel][slotrank].lanes[lane].
1013 timA + shift -
1014 (post_timA_min_high << 6)) & 0x1c0) << 10)
1015 | ((ctrl->timings[channel][slotrank].lanes[lane].
1016 falling + shift) << 20));
1017
Felix Heldfb19c8a2020-01-14 21:27:59 +01001018 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001019 (((ctrl->timings[channel][slotrank].lanes[lane].
1020 timC + shift) & 0x3f)
1021 |
1022 (((ctrl->timings[channel][slotrank].lanes[lane].
1023 timB + shift) & 0x3f) << 8)
1024 |
1025 (((ctrl->timings[channel][slotrank].lanes[lane].
1026 timB + shift) & 0x1c0) << 9)
1027 |
1028 (((ctrl->timings[channel][slotrank].lanes[lane].
1029 timC + shift) & 0x40) << 13));
1030 }
1031 }
Angel Pons88521882020-01-05 20:21:20 +01001032 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1033 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001034}
1035
Angel Pons88521882020-01-05 20:21:20 +01001036static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001037{
Angel Pons88521882020-01-05 20:21:20 +01001038 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001039
1040 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001041 write MR3 MPR enable
1042 in this mode only RD and RDA are allowed
1043 all reads return a predefined pattern */
1044 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1045 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = (0xc01 | (ctrl->tMOD << 16));
Angel Pons88521882020-01-05 20:21:20 +01001046 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001047 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001048
1049 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001050 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1051 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4040c01;
Angel Pons88521882020-01-05 20:21:20 +01001052 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001053 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001054
1055 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001056 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1057 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x100f | ((ctrl->CAS + 36) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001058 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001059 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001060
1061 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001062 write MR3 MPR disable */
1063 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1064 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001065 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001066 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001067
Angel Pons7c49cb82020-03-16 23:17:32 +01001068 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001069 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001070
Angel Pons88521882020-01-05 20:21:20 +01001071 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001072}
1073
Angel Pons7c49cb82020-03-16 23:17:32 +01001074static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank, int lane)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001075{
1076 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Angel Pons7c49cb82020-03-16 23:17:32 +01001077
1078 return (MCHBAR32(lane_base[lane] +
1079 GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >> (timA % 32)) & 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001080}
1081
1082struct run {
1083 int middle;
1084 int end;
1085 int start;
1086 int all;
1087 int length;
1088};
1089
1090static struct run get_longest_zero_run(int *seq, int sz)
1091{
1092 int i, ls;
1093 int bl = 0, bs = 0;
1094 struct run ret;
1095
1096 ls = 0;
1097 for (i = 0; i < 2 * sz; i++)
1098 if (seq[i % sz]) {
1099 if (i - ls > bl) {
1100 bl = i - ls;
1101 bs = ls;
1102 }
1103 ls = i + 1;
1104 }
1105 if (bl == 0) {
1106 ret.middle = sz / 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001107 ret.start = 0;
1108 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001109 ret.length = sz;
Angel Pons7c49cb82020-03-16 23:17:32 +01001110 ret.all = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001111 return ret;
1112 }
1113
Angel Pons7c49cb82020-03-16 23:17:32 +01001114 ret.start = bs % sz;
1115 ret.end = (bs + bl - 1) % sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001116 ret.middle = (bs + (bl - 1) / 2) % sz;
1117 ret.length = bl;
Angel Pons7c49cb82020-03-16 23:17:32 +01001118 ret.all = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001119
1120 return ret;
1121}
1122
Angel Pons7c49cb82020-03-16 23:17:32 +01001123static void discover_timA_coarse(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001124{
1125 int timA;
1126 int statistics[NUM_LANES][128];
1127 int lane;
1128
1129 for (timA = 0; timA < 128; timA++) {
1130 FOR_ALL_LANES {
1131 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1132 }
1133 program_timings(ctrl, channel);
1134
1135 test_timA(ctrl, channel, slotrank);
1136
1137 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001138 statistics[lane][timA] = !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001139 }
1140 }
1141 FOR_ALL_LANES {
1142 struct run rn = get_longest_zero_run(statistics[lane], 128);
1143 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1144 upperA[lane] = rn.end;
1145 if (upperA[lane] < rn.middle)
1146 upperA[lane] += 128;
Angel Pons7c49cb82020-03-16 23:17:32 +01001147
Patrick Rudolph368b6152016-11-25 16:36:52 +01001148 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001149 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001150 }
1151}
1152
Angel Pons7c49cb82020-03-16 23:17:32 +01001153static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001154{
1155 int timA_delta;
1156 int statistics[NUM_LANES][51];
1157 int lane, i;
1158
1159 memset(statistics, 0, sizeof(statistics));
1160
1161 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01001162
1163 FOR_ALL_LANES {
1164 ctrl->timings[channel][slotrank].lanes[lane].timA
1165 = upperA[lane] + timA_delta + 0x40;
1166 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001167 program_timings(ctrl, channel);
1168
1169 for (i = 0; i < 100; i++) {
1170 test_timA(ctrl, channel, slotrank);
1171 FOR_ALL_LANES {
1172 statistics[lane][timA_delta + 25] +=
Angel Pons7c49cb82020-03-16 23:17:32 +01001173 does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001174 }
1175 }
1176 }
1177 FOR_ALL_LANES {
1178 int last_zero, first_all;
1179
1180 for (last_zero = -25; last_zero <= 25; last_zero++)
1181 if (statistics[lane][last_zero + 25])
1182 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01001183
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001184 last_zero--;
1185 for (first_all = -25; first_all <= 25; first_all++)
1186 if (statistics[lane][first_all + 25] == 100)
1187 break;
1188
Angel Pons7c49cb82020-03-16 23:17:32 +01001189 printram("lane %d: %d, %d\n", lane, last_zero, first_all);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001190
1191 ctrl->timings[channel][slotrank].lanes[lane].timA =
Angel Pons7c49cb82020-03-16 23:17:32 +01001192 (last_zero + first_all) / 2 + upperA[lane];
1193
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001194 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01001195 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001196 }
1197}
1198
Angel Pons891f2bc2020-01-10 01:27:28 +01001199static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001200{
1201 int works[NUM_LANES];
1202 int lane;
Angel Pons7c49cb82020-03-16 23:17:32 +01001203
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001204 while (1) {
1205 int all_works = 1, some_works = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001206
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001207 program_timings(ctrl, channel);
1208 test_timA(ctrl, channel, slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001209
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001210 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001211 works[lane] = !does_lane_work(ctrl, channel, slotrank, lane);
1212
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001213 if (works[lane])
1214 some_works = 1;
1215 else
1216 all_works = 0;
1217 }
1218 if (all_works)
1219 return 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001220
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001221 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001222 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001223 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1224 channel, slotrank);
1225 return MAKE_ERR;
1226 }
Angel Pons88521882020-01-05 20:21:20 +01001227 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001228 printram("4024 -= 2;\n");
1229 continue;
1230 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001231 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001232 printram("4028 += 2;\n");
Angel Pons7c49cb82020-03-16 23:17:32 +01001233
Felix Heldef4fe3e2019-12-31 14:15:05 +01001234 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001235 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1236 channel, slotrank);
1237 return MAKE_ERR;
1238 }
1239 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001240 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001241 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001242 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001243 }
1244 }
1245 return 0;
1246}
1247
1248struct timA_minmax {
1249 int timA_min_high, timA_max_high;
1250};
1251
Angel Pons88521882020-01-05 20:21:20 +01001252static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001253 struct timA_minmax *mnmx)
1254{
1255 int lane;
1256 mnmx->timA_min_high = 7;
1257 mnmx->timA_max_high = 0;
1258
1259 FOR_ALL_LANES {
1260 if (mnmx->timA_min_high >
1261 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1262 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001263 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001264 if (mnmx->timA_max_high <
1265 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1266 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001267 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001268 }
1269}
1270
Angel Pons88521882020-01-05 20:21:20 +01001271static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001272 struct timA_minmax *mnmx)
1273{
1274 struct timA_minmax post;
1275 int shift_402x = 0;
1276
Angel Pons7c49cb82020-03-16 23:17:32 +01001277 /* Get changed maxima */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001278 pre_timA_change(ctrl, channel, slotrank, &post);
1279
1280 if (mnmx->timA_max_high - mnmx->timA_min_high <
1281 post.timA_max_high - post.timA_min_high)
1282 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001283
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001284 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1285 post.timA_max_high - post.timA_min_high)
1286 shift_402x = -1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001287
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001288 else
1289 shift_402x = 0;
1290
Felix Heldef4fe3e2019-12-31 14:15:05 +01001291 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001292 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001293 printram("4024 += %d;\n", shift_402x);
1294 printram("4028 += %d;\n", shift_402x);
1295}
1296
Angel Pons7c49cb82020-03-16 23:17:32 +01001297/*
1298 * Compensate the skew between DQS and DQs.
1299 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001300 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1301 * The controller has to measure and compensate this skew for every byte-lane. By delaying
Angel Pons7c49cb82020-03-16 23:17:32 +01001302 * either all DQ signals or DQS signal, a full phase shift can be introduced. It is assumed
Angel Pons891f2bc2020-01-10 01:27:28 +01001303 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001304 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001305 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1306 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1307 * over all possible values to do a full phase shift and issues read commands. With DQS and
Angel Pons7c49cb82020-03-16 23:17:32 +01001308 * DQ in phase the data being read is expected to alternate on every byte:
1309 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001310 * 0xFF 0x00 0xFF ...
Angel Pons7c49cb82020-03-16 23:17:32 +01001311 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001312 * Once the controller has detected this pattern a bit in the result register is set for the
1313 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001314 */
Angel Pons88521882020-01-05 20:21:20 +01001315int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001316{
1317 int channel, slotrank, lane;
1318 int err;
1319
1320 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1321 int all_high, some_high;
1322 int upperA[NUM_LANES];
1323 struct timA_minmax mnmx;
1324
Angel Pons88521882020-01-05 20:21:20 +01001325 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001326
Felix Held2bb3cdf2018-07-28 00:23:59 +02001327 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001328 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1329 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001330 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001331 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001332
Angel Pons7c49cb82020-03-16 23:17:32 +01001333 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001334 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001335
Angel Pons88521882020-01-05 20:21:20 +01001336 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001337
Felix Heldef4fe3e2019-12-31 14:15:05 +01001338 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001339 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001340 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001341
Felix Held2bb3cdf2018-07-28 00:23:59 +02001342 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001343
Felix Held2bb3cdf2018-07-28 00:23:59 +02001344 all_high = 1;
1345 some_high = 0;
1346 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001347 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001348 some_high = 1;
1349 else
1350 all_high = 0;
1351 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001352
1353 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001354 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001355 printram("4028--;\n");
1356 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001357 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001358 upperA[lane] -= 0x40;
1359
1360 }
1361 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001362 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001363 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001364 printram("4024++;\n");
1365 printram("4028++;\n");
1366 }
1367
1368 program_timings(ctrl, channel);
1369
1370 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1371
1372 err = discover_402x(ctrl, channel, slotrank, upperA);
1373 if (err)
1374 return err;
1375
1376 post_timA_change(ctrl, channel, slotrank, &mnmx);
1377 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1378
1379 discover_timA_fine(ctrl, channel, slotrank, upperA);
1380
1381 post_timA_change(ctrl, channel, slotrank, &mnmx);
1382 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1383
1384 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001385 ctrl->timings[channel][slotrank].lanes[lane].timA -=
1386 mnmx.timA_min_high * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001387 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001388 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001389 printram("4028 -= %d;\n", mnmx.timA_min_high);
1390
1391 post_timA_change(ctrl, channel, slotrank, &mnmx);
1392
1393 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001394 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001395 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001396
1397 printram("final results:\n");
1398 FOR_ALL_LANES
Angel Pons7c49cb82020-03-16 23:17:32 +01001399 printram("Aval: %d, %d, %d: %x\n", channel, slotrank, lane,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001400 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001401
Angel Pons88521882020-01-05 20:21:20 +01001402 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001403
1404 toggle_io_reset();
1405 }
1406
1407 FOR_ALL_POPULATED_CHANNELS {
1408 program_timings(ctrl, channel);
1409 }
1410 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001411 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001412 }
1413 return 0;
1414}
1415
Angel Pons88521882020-01-05 20:21:20 +01001416static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001417{
1418 int lane;
1419
1420 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001421 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1422 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001423 }
1424
Angel Pons88521882020-01-05 20:21:20 +01001425 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001426
1427 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001428 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
1429 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
1430 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | 4 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001431 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | (6 << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +01001432 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001433
1434 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001435 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f207;
1436 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8041001;
Angel Pons88521882020-01-05 20:21:20 +01001437 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001438 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001439
1440 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01001441 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f201;
1442 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x80411f4;
Angel Pons88521882020-01-05 20:21:20 +01001443 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001444 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001445
1446 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001447 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f207;
1448 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
1449 0x08000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001450 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001451 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001452
Angel Pons7c49cb82020-03-16 23:17:32 +01001453 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001454 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001455
Angel Pons88521882020-01-05 20:21:20 +01001456 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001457
1458 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001459 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1460 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001461 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001462 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001463
1464 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001465 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f006;
1466 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
1467 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) | 8 | (ctrl->CAS << 16);
Angel Pons88521882020-01-05 20:21:20 +01001468 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001469 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001470
1471 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001472 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1473 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001474 0x40011f4 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001475 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001476 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001477
1478 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001479 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
1480 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001481 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001482 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001483
Angel Pons7c49cb82020-03-16 23:17:32 +01001484 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001485 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001486
Angel Pons88521882020-01-05 20:21:20 +01001487 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001488}
1489
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001490static void timC_threshold_process(int *data, const int count)
1491{
1492 int min = data[0];
1493 int max = min;
1494 int i;
1495 for (i = 1; i < count; i++) {
1496 if (min > data[i])
1497 min = data[i];
Angel Pons7c49cb82020-03-16 23:17:32 +01001498
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001499 if (max < data[i])
1500 max = data[i];
1501 }
Angel Pons7c49cb82020-03-16 23:17:32 +01001502 int threshold = min / 2 + max / 2;
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001503 for (i = 0; i < count; i++)
1504 data[i] = data[i] > threshold;
Angel Pons7c49cb82020-03-16 23:17:32 +01001505
Angel Pons891f2bc2020-01-10 01:27:28 +01001506 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001507}
1508
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001509static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1510{
1511 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01001512 int stats[NUM_LANES][MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001513 int lane;
1514
Angel Pons88521882020-01-05 20:21:20 +01001515 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001516
1517 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001518 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1519 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001520 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001521 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001522
Angel Pons7c49cb82020-03-16 23:17:32 +01001523 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001524 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001525
1526 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001527 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001528 program_timings(ctrl, channel);
1529
1530 test_timC(ctrl, channel, slotrank);
1531
1532 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001533 stats[lane][timC] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001534 }
1535 }
1536 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001537 struct run rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1538
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001539 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001540 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1541 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001542 /*
1543 * With command training not being done yet, the lane can be erroneous.
1544 * Take the average as reference and try again to find a run.
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001545 */
Angel Pons7c49cb82020-03-16 23:17:32 +01001546 timC_threshold_process(stats[lane], ARRAY_SIZE(stats[lane]));
1547 rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1548
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001549 if (rn.all || rn.length < 8) {
1550 printk(BIOS_EMERG, "timC recovery failed\n");
1551 return MAKE_ERR;
1552 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001553 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001554 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001555 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001556 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001557 }
1558 return 0;
1559}
1560
Angel Pons88521882020-01-05 20:21:20 +01001561static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001562{
1563 int channel, ret = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001564
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001565 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1566 ret++;
Angel Pons7c49cb82020-03-16 23:17:32 +01001567
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001568 return ret;
1569}
1570
Angel Pons88521882020-01-05 20:21:20 +01001571static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001572{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301573 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001574 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Angel Pons7c49cb82020-03-16 23:17:32 +01001575
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001576 for (j = 0; j < 16; j++)
1577 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
Angel Pons7c49cb82020-03-16 23:17:32 +01001578
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001579 sfence();
1580}
1581
Angel Pons88521882020-01-05 20:21:20 +01001582static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001583{
1584 int ret = 0;
1585 int channel;
1586 FOR_ALL_POPULATED_CHANNELS ret++;
1587 return ret;
1588}
1589
Angel Pons88521882020-01-05 20:21:20 +01001590static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001591{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301592 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001593 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301594 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01001595
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001596 for (j = 0; j < 16; j++)
1597 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
Angel Pons7c49cb82020-03-16 23:17:32 +01001598
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001599 for (j = 0; j < 16; j++)
1600 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
Angel Pons7c49cb82020-03-16 23:17:32 +01001601
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001602 sfence();
1603}
1604
Angel Pons88521882020-01-05 20:21:20 +01001605static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001606{
1607 int channel, slotrank, lane;
1608
1609 FOR_ALL_POPULATED_CHANNELS {
1610 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001611 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1612 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001613 }
1614
1615 program_timings(ctrl, channel);
1616
1617 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001618 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001619
1620 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001621 write MR3 MPR enable
1622 in this mode only RD and RDA are allowed
1623 all reads return a predefined pattern */
1624 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1625 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001626 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001627 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001628 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001629 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001630
1631 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001632 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1633 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001634 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001635 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001636
1637 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001638 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1639 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001640 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001641 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001642 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001643 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001644
1645 /* DRAM command MRS
1646 * write MR3 MPR disable */
Angel Pons7c49cb82020-03-16 23:17:32 +01001647 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1648 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001649 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001650 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001651 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001652 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001653
Angel Pons7c49cb82020-03-16 23:17:32 +01001654 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001655 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001656
Angel Pons88521882020-01-05 20:21:20 +01001657 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001658 }
1659
1660 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001661 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1662 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001663 }
1664
1665 program_timings(ctrl, channel);
1666
1667 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001668 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001669 /* DRAM command MRS
1670 * write MR3 MPR enable
1671 * in this mode only RD and RDA are allowed
1672 * all reads return a predefined pattern */
Angel Pons7c49cb82020-03-16 23:17:32 +01001673 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1674 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001675 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001676 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001677 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001678 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001679
1680 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001681 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1682 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001683 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001684 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001685
1686 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001687 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1688 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001689 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001690 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001691 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001692 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001693
1694 /* DRAM command MRS
1695 * write MR3 MPR disable */
Angel Pons7c49cb82020-03-16 23:17:32 +01001696 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1697 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001698 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001699 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001700 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001701 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001702
Angel Pons7c49cb82020-03-16 23:17:32 +01001703 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001704 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001705
Angel Pons88521882020-01-05 20:21:20 +01001706 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001707 }
1708 }
1709}
1710
Angel Pons88521882020-01-05 20:21:20 +01001711static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001712{
1713 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001714 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001715
Angel Pons88521882020-01-05 20:21:20 +01001716 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001717 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001718 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f207;
1719 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001720 0x8000c01 | ((ctrl->CWL + ctrl->tWLO) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001721 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 8 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001722 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001723
1724 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001725 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f107;
1726 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4000c01 | ((ctrl->CAS + 38) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001727 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01001728 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001729
Angel Pons7c49cb82020-03-16 23:17:32 +01001730 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001731 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001732
Angel Pons88521882020-01-05 20:21:20 +01001733 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001734
1735 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001736 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001737}
1738
1739static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1740{
1741 int timB;
1742 int statistics[NUM_LANES][128];
1743 int lane;
1744
Angel Pons88521882020-01-05 20:21:20 +01001745 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001746
1747 for (timB = 0; timB < 128; timB++) {
1748 FOR_ALL_LANES {
1749 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1750 }
1751 program_timings(ctrl, channel);
1752
1753 test_timB(ctrl, channel, slotrank);
1754
1755 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001756 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1757 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1758 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001759 }
1760 }
1761 FOR_ALL_LANES {
1762 struct run rn = get_longest_zero_run(statistics[lane], 128);
Angel Pons7c49cb82020-03-16 23:17:32 +01001763 /*
1764 * timC is a direct function of timB's 6 LSBs. Some tests increments the value
1765 * of timB by a small value, which might cause the 6-bit value to overflow if
1766 * it's close to 0x3f. Increment the value by a small offset if it's likely
1767 * to overflow, to make sure it won't overflow while running tests and bricks
1768 * the system due to a non matching timC.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001769 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001770 * TODO: find out why some tests (edge write discovery) increment timB.
1771 */
1772 if ((rn.start & 0x3f) == 0x3e)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001773 rn.start += 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001774 else if ((rn.start & 0x3f) == 0x3f)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001775 rn.start += 1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001776
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001777 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1778 if (rn.all) {
1779 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1780 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001781
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001782 return MAKE_ERR;
1783 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001784 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1785 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001786 }
1787 return 0;
1788}
1789
1790static int get_timB_high_adjust(u64 val)
1791{
1792 int i;
1793
1794 /* good */
1795 if (val == 0xffffffffffffffffLL)
1796 return 0;
1797
1798 if (val >= 0xf000000000000000LL) {
1799 /* needs negative adjustment */
1800 for (i = 0; i < 8; i++)
1801 if (val << (8 * (7 - i) + 4))
1802 return -i;
1803 } else {
1804 /* needs positive adjustment */
1805 for (i = 0; i < 8; i++)
1806 if (val >> (8 * (7 - i) + 4))
1807 return i;
1808 }
1809 return 8;
1810}
1811
Angel Pons88521882020-01-05 20:21:20 +01001812static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001813{
1814 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001815 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001816 FOR_ALL_POPULATED_CHANNELS {
1817 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001818 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001819 }
1820 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1821
Angel Pons88521882020-01-05 20:21:20 +01001822 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001823
Angel Pons88521882020-01-05 20:21:20 +01001824 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001825
1826 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001827 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
1828 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001829 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001830 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001831
1832 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001833 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f207;
1834 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8040c01;
Angel Pons88521882020-01-05 20:21:20 +01001835 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001836 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001837
1838 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01001839 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f201;
1840 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x8041003;
Angel Pons88521882020-01-05 20:21:20 +01001841 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001842 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x3e2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001843
1844 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001845 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f207;
1846 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001847 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001848 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001849 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001850
Angel Pons7c49cb82020-03-16 23:17:32 +01001851 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001852 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001853
Angel Pons88521882020-01-05 20:21:20 +01001854 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001855
1856 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001857 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1858 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | ((ctrl->tRP) << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01001859 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001860 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001861
1862 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001863 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f006;
1864 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0xc01 | ((ctrl->tRCD) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001865 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001866 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001867
1868 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001869 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x3f105;
1870 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x4000c01 | ((ctrl->tRP +
Angel Pons88521882020-01-05 20:21:20 +01001871 ctrl->timings[channel][slotrank].roundtrip_latency +
Felix Heldef4fe3e2019-12-31 14:15:05 +01001872 ctrl->timings[channel][slotrank].io_latency) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001873 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60008;
Angel Pons7c49cb82020-03-16 23:17:32 +01001874 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001875
Angel Pons7c49cb82020-03-16 23:17:32 +01001876 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001877 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Felix Held9cf1dd22018-07-31 14:52:40 +02001878
Angel Pons88521882020-01-05 20:21:20 +01001879 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001880 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001881 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01001882 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01001883 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001884 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
1885 ctrl->timings[channel][slotrank].lanes[lane].timB +=
1886 get_timB_high_adjust(res) * 64;
1887
1888 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01001889 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
1890 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001891 }
1892 }
Angel Pons88521882020-01-05 20:21:20 +01001893 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001894}
1895
Angel Pons88521882020-01-05 20:21:20 +01001896static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001897{
1898 int slotrank;
1899
Angel Pons88521882020-01-05 20:21:20 +01001900 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001901
1902 /* choose an existing rank. */
1903 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
1904
1905 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001906 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
1907 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01001908 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001909 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001910
Angel Pons7c49cb82020-03-16 23:17:32 +01001911 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001912 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001913
Angel Pons88521882020-01-05 20:21:20 +01001914 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001915}
1916
Angel Pons7c49cb82020-03-16 23:17:32 +01001917/*
1918 * Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001919 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001920 * Since DDR3 uses a fly-by topology, the data and strobes signals reach the chips at different
1921 * times with respect to command, address and clock signals. By delaying either all DQ/DQS or
1922 * all CMD/ADDR/CLK signals, a full phase shift can be introduced. It is assumed that the
1923 * CLK/ADDR/CMD signals have the same routing delay.
1924 *
1925 * To find the required phase shift the DRAM is placed in "write leveling" mode. In this mode,
1926 * the DRAM-chip samples the CLK on every DQS edge and feeds back the sampled value on the data
1927 * lanes (DQ).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001928 */
Angel Pons88521882020-01-05 20:21:20 +01001929int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001930{
1931 int channel, slotrank, lane;
1932 int err;
1933
1934 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001935 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001936
1937 FOR_ALL_POPULATED_CHANNELS {
1938 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001939 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001940 }
1941
Angel Pons7c49cb82020-03-16 23:17:32 +01001942 /* Refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01001943 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001944 FOR_ALL_POPULATED_CHANNELS {
1945 write_op(ctrl, channel);
1946 }
1947
Angel Pons7c49cb82020-03-16 23:17:32 +01001948 /* Enable write leveling on all ranks
1949 Disable all DQ outputs
1950 Only NOP is allowed in this mode */
1951 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
1952 write_mrreg(ctrl, channel, slotrank, 1,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001953 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001954
Angel Pons88521882020-01-05 20:21:20 +01001955 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001956
1957 toggle_io_reset();
1958
Angel Pons7c49cb82020-03-16 23:17:32 +01001959 /* Set any valid value for timB, it gets corrected later */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001960 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1961 err = discover_timB(ctrl, channel, slotrank);
1962 if (err)
1963 return err;
1964 }
1965
Angel Pons7c49cb82020-03-16 23:17:32 +01001966 /* Disable write leveling on all ranks */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001967 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
Angel Pons7c49cb82020-03-16 23:17:32 +01001968 write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001969
Angel Pons88521882020-01-05 20:21:20 +01001970 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001971
1972 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001973 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001974
Angel Pons7c49cb82020-03-16 23:17:32 +01001975 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01001976 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001977
1978 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001979 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
1980 MCHBAR32(IOSAV_STATUS_ch(channel));
1981 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001982
1983 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01001984 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
1985 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
Angel Pons88521882020-01-05 20:21:20 +01001986 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001987 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001988
Angel Pons7c49cb82020-03-16 23:17:32 +01001989 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001990 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001991
Angel Pons88521882020-01-05 20:21:20 +01001992 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001993 }
1994
1995 toggle_io_reset();
1996
1997 printram("CPE\n");
1998 precharge(ctrl);
1999 printram("CPF\n");
2000
2001 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002002 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002003 }
2004
2005 FOR_ALL_POPULATED_CHANNELS {
2006 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01002007 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002008 }
2009
2010 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2011 err = discover_timC(ctrl, channel, slotrank);
2012 if (err)
2013 return err;
2014 }
2015
2016 FOR_ALL_POPULATED_CHANNELS
2017 program_timings(ctrl, channel);
2018
2019 /* measure and adjust timB timings */
2020 adjust_high_timB(ctrl);
2021
2022 FOR_ALL_POPULATED_CHANNELS
2023 program_timings(ctrl, channel);
2024
2025 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002026 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002027 }
2028 return 0;
2029}
2030
Angel Pons88521882020-01-05 20:21:20 +01002031static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002032{
2033 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2034 int timC_delta;
2035 int lanes_ok = 0;
2036 int ctr = 0;
2037 int lane;
2038
2039 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2040 FOR_ALL_LANES {
2041 ctrl->timings[channel][slotrank].lanes[lane].timC =
2042 saved_rt.lanes[lane].timC + timC_delta;
2043 }
2044 program_timings(ctrl, channel);
2045 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002046 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002047 }
2048
Angel Pons88521882020-01-05 20:21:20 +01002049 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002050
Angel Pons88521882020-01-05 20:21:20 +01002051 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002052 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002053 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
2054 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002055 ((MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002056 | 8 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002057 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002058 (slotrank << 24) | ctr | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002059 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Felix Held9fe248f2018-07-31 20:59:45 +02002060
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002061 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002062 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2063 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002064 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002065 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
2066 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
Angel Pons7c49cb82020-03-16 23:17:32 +01002067 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002068
2069 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002070 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2071 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002072 0x4001020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002073 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
2074 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
Angel Pons7c49cb82020-03-16 23:17:32 +01002075 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002076
2077 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002078 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2079 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xf1001;
Angel Pons88521882020-01-05 20:21:20 +01002080 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002081 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002082
Angel Pons7c49cb82020-03-16 23:17:32 +01002083 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002084 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002085
Angel Pons88521882020-01-05 20:21:20 +01002086 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002087 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002088 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002089
2090 if (r32 == 0)
2091 lanes_ok |= 1 << lane;
2092 }
2093 ctr++;
2094 if (lanes_ok == ((1 << NUM_LANES) - 1))
2095 break;
2096 }
2097
2098 ctrl->timings[channel][slotrank] = saved_rt;
2099
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002100 return lanes_ok != ((1 << NUM_LANES) - 1);
2101}
2102
2103#include "raminit_patterns.h"
2104
Angel Pons88521882020-01-05 20:21:20 +01002105static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002106{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302107 unsigned int i, j;
Angel Pons7c49cb82020-03-16 23:17:32 +01002108 unsigned int offset = get_precedening_channels(ctrl, channel) * 0x40;
2109 unsigned int step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002110
2111 if (patno) {
2112 u8 base8 = 0x80 >> ((patno - 1) % 8);
2113 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2114 for (i = 0; i < 32; i++) {
2115 for (j = 0; j < 16; j++) {
2116 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01002117
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002118 if (invert[patno - 1][i] & (1 << (j / 2)))
2119 val = ~val;
Angel Pons7c49cb82020-03-16 23:17:32 +01002120
2121 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002122 }
2123 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002124 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002125 for (i = 0; i < ARRAY_SIZE(pattern); i++) {
2126 for (j = 0; j < 16; j++) {
2127 const u32 val = pattern[i][j];
2128 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
2129 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002130 }
2131 sfence();
2132 }
2133}
2134
Angel Pons88521882020-01-05 20:21:20 +01002135static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002136{
2137 int channel, slotrank;
2138
2139 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002140 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002141
Angel Pons7c49cb82020-03-16 23:17:32 +01002142 /* Choose an existing rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002143 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2144
2145 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01002146 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
2147 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002148 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002149 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002150
Angel Pons7c49cb82020-03-16 23:17:32 +01002151 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002152 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002153
Angel Pons88521882020-01-05 20:21:20 +01002154 wait_for_iosav(channel);
2155 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002156 }
2157
2158 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002159 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002160 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002161 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002162
2163 /* choose an existing rank. */
2164 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2165
2166 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01002167 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
2168 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002169 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002170 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002171
Angel Pons7c49cb82020-03-16 23:17:32 +01002172 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002173 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002174
Angel Pons88521882020-01-05 20:21:20 +01002175 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002176 }
2177
Angel Pons7c49cb82020-03-16 23:17:32 +01002178 /* JEDEC reset */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002179 dram_jedecreset(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01002180
2181 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002182 dram_mrscommands(ctrl);
2183
2184 toggle_io_reset();
2185}
2186
2187#define MIN_C320C_LEN 13
2188
2189static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2190{
2191 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2192 int slotrank;
2193 int c320c;
2194 int stat[NUM_SLOTRANKS][256];
2195 int delta = 0;
2196
2197 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2198
2199 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002200 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002201 }
2202
2203 ctrl->cmd_stretch[channel] = cmd_stretch;
2204
Angel Pons88521882020-01-05 20:21:20 +01002205 MCHBAR32(TC_RAP_ch(channel)) =
Angel Pons7c49cb82020-03-16 23:17:32 +01002206 (ctrl->tRRD << 0)
2207 | (ctrl->tRTP << 4)
2208 | (ctrl->tCKE << 8)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002209 | (ctrl->tWTR << 12)
2210 | (ctrl->tFAW << 16)
Angel Pons7c49cb82020-03-16 23:17:32 +01002211 | (ctrl->tWR << 24)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002212 | (ctrl->cmd_stretch[channel] << 30);
2213
2214 if (ctrl->cmd_stretch[channel] == 2)
2215 delta = 2;
2216 else if (ctrl->cmd_stretch[channel] == 0)
2217 delta = 4;
2218
2219 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002220 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002221 }
2222
2223 for (c320c = -127; c320c <= 127; c320c++) {
2224 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002225 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002226 }
2227 program_timings(ctrl, channel);
2228 reprogram_320c(ctrl);
2229 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002230 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002231 }
2232 }
2233 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002234 struct run rn = get_longest_zero_run(stat[slotrank], 255);
2235
Angel Pons88521882020-01-05 20:21:20 +01002236 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002237 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2238 channel, slotrank, rn.start, rn.middle, rn.end);
Angel Pons7c49cb82020-03-16 23:17:32 +01002239
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002240 if (rn.all || rn.length < MIN_C320C_LEN) {
2241 FOR_ALL_POPULATED_RANKS {
2242 ctrl->timings[channel][slotrank] =
2243 saved_timings[channel][slotrank];
2244 }
2245 return MAKE_ERR;
2246 }
2247 }
2248
2249 return 0;
2250}
2251
Angel Pons7c49cb82020-03-16 23:17:32 +01002252/*
2253 * Adjust CMD phase shift and try multiple command rates.
2254 * A command rate of 2T doubles the time needed for address and command decode.
2255 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002256int command_training(ramctr_timing *ctrl)
2257{
2258 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002259
2260 FOR_ALL_POPULATED_CHANNELS {
2261 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002262 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002263 }
2264
2265 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002266 int cmdrate, err;
2267
2268 /*
2269 * Dual DIMM per channel:
Angel Pons7c49cb82020-03-16 23:17:32 +01002270 * Issue:
2271 * While c320c discovery seems to succeed raminit will fail in write training.
2272 *
2273 * Workaround:
2274 * Skip 1T in dual DIMM mode, that's only supported by a few DIMMs.
2275 * Only try 1T mode for XMP DIMMs that request it in dual DIMM mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002276 *
2277 * Single DIMM per channel:
2278 * Try command rate 1T and 2T
2279 */
2280 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002281 if (ctrl->tCMD)
2282 /* XMP gives the CMD rate in clock ticks, not ns */
2283 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002284
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002285 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002286 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2287
2288 if (!err)
2289 break;
2290 }
2291
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002292 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002293 printk(BIOS_EMERG, "c320c discovery failed\n");
2294 return err;
2295 }
2296
Angel Pons891f2bc2020-01-10 01:27:28 +01002297 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002298 }
2299
2300 FOR_ALL_POPULATED_CHANNELS
2301 program_timings(ctrl, channel);
2302
2303 reprogram_320c(ctrl);
2304 return 0;
2305}
2306
Angel Pons891f2bc2020-01-10 01:27:28 +01002307static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002308{
2309 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002310 int stats[NUM_LANES][MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002311 int lane;
2312
2313 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2314 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002315 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
Angel Pons891f2bc2020-01-10 01:27:28 +01002316 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002317 }
2318 program_timings(ctrl, channel);
2319
2320 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002321 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2322 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002323 }
2324
Angel Pons88521882020-01-05 20:21:20 +01002325 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002326
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002327 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002328 write MR3 MPR enable
2329 in this mode only RD and RDA are allowed
2330 all reads return a predefined pattern */
2331 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2332 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002333 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002334 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002335
2336 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002337 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2338 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x40411f4;
Angel Pons88521882020-01-05 20:21:20 +01002339 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002340 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002341
2342 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002343 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2344 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002345 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002346 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002347
2348 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002349 MR3 disable MPR */
2350 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2351 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002352 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002353 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002354
Angel Pons7c49cb82020-03-16 23:17:32 +01002355 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002356 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002357
Angel Pons88521882020-01-05 20:21:20 +01002358 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002359
2360 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002361 stats[lane][edge] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002362 }
2363 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002364
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002365 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002366 struct run rn = get_longest_zero_run(stats[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002367 edges[lane] = rn.middle;
Angel Pons7c49cb82020-03-16 23:17:32 +01002368
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002369 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002370 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n", channel,
2371 slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002372 return MAKE_ERR;
2373 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002374 printram("eval %d, %d, %d: %02x\n", channel, slotrank, lane, edges[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002375 }
2376 return 0;
2377}
2378
2379int discover_edges(ramctr_timing *ctrl)
2380{
2381 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2382 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2383 int channel, slotrank, lane;
2384 int err;
2385
Angel Pons88521882020-01-05 20:21:20 +01002386 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002387
2388 toggle_io_reset();
2389
2390 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002391 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002392 }
2393
2394 FOR_ALL_POPULATED_CHANNELS {
2395 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002396 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002397 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002398 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002399 }
2400
2401 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002402 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2403 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002404 }
2405
2406 program_timings(ctrl, channel);
2407
2408 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002409 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002410
2411 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002412 MR3 enable MPR
2413 write MR3 MPR enable
2414 in this mode only RD and RDA are allowed
2415 all reads return a predefined pattern */
2416 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2417 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002418 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002419 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002420 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002421 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002422
2423 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002424 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2425 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01002426 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002427 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002428
2429 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002430 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2431 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002432 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002433 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002434 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002435 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002436
2437 /* DRAM command MRS
2438 * MR3 disable MPR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002439 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2440 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002441 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002442 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002443 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002444 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02002445
Angel Pons7c49cb82020-03-16 23:17:32 +01002446 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002447 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002448
Angel Pons88521882020-01-05 20:21:20 +01002449 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002450 }
2451
2452 /* XXX: check any measured value ? */
2453
2454 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002455 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
Angel Pons7c49cb82020-03-16 23:17:32 +01002456 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002457 }
2458
2459 program_timings(ctrl, channel);
2460
2461 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002462 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002463
2464 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002465 MR3 enable MPR
2466 write MR3 MPR enable
2467 in this mode only RD and RDA are allowed
2468 all reads return a predefined pattern */
2469 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2470 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002471 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002472 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002473 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002474 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002475
2476 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002477 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2478 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons88521882020-01-05 20:21:20 +01002479 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Angel Pons63ae8de2020-01-10 02:03:47 +01002480 (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002481 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002482
2483 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002484 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2485 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002486 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002487 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002488 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002489 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002490
2491 /* DRAM command MRS
2492 * MR3 disable MPR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002493 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2494 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002495 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002496 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002497 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002498 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002499
Angel Pons7c49cb82020-03-16 23:17:32 +01002500 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002501 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002502
Angel Pons88521882020-01-05 20:21:20 +01002503 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002504 }
2505
2506 /* XXX: check any measured value ? */
2507
2508 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002509 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002510 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002511 }
2512
2513 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002514 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002515 }
2516
2517 /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */
Angel Pons88521882020-01-05 20:21:20 +01002518 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2519 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002520
2521 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2522 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002523 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002524 if (err)
2525 return err;
2526 }
2527
Angel Pons88521882020-01-05 20:21:20 +01002528 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2529 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002530
2531 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2532 err = discover_edges_real(ctrl, channel, slotrank,
2533 rising_edges[channel][slotrank]);
2534 if (err)
2535 return err;
2536 }
2537
Angel Pons88521882020-01-05 20:21:20 +01002538 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002539
2540 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2541 ctrl->timings[channel][slotrank].lanes[lane].falling =
2542 falling_edges[channel][slotrank][lane];
2543 ctrl->timings[channel][slotrank].lanes[lane].rising =
2544 rising_edges[channel][slotrank][lane];
2545 }
2546
2547 FOR_ALL_POPULATED_CHANNELS {
2548 program_timings(ctrl, channel);
2549 }
2550
2551 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002552 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002553 }
2554 return 0;
2555}
2556
Angel Pons7c49cb82020-03-16 23:17:32 +01002557static int discover_edges_write_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002558{
2559 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002560 u32 raw_stats[MAX_EDGE_TIMING + 1];
2561 int stats[MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002562 const int reg3000b24[] = { 0, 0xc, 0x2c };
2563 int lane, i;
2564 int lower[NUM_LANES];
2565 int upper[NUM_LANES];
2566 int pat;
2567
2568 FOR_ALL_LANES {
2569 lower[lane] = 0;
2570 upper[lane] = MAX_EDGE_TIMING;
2571 }
2572
2573 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002574 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002575 printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
2576
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002577 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2578 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002579 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002580 printram("using pattern %d\n", pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002581
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002582 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2583 FOR_ALL_LANES {
2584 ctrl->timings[channel][slotrank].lanes[lane].
2585 rising = edge;
2586 ctrl->timings[channel][slotrank].lanes[lane].
2587 falling = edge;
2588 }
2589 program_timings(ctrl, channel);
2590
2591 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002592 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2593 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002594 }
Angel Pons88521882020-01-05 20:21:20 +01002595 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002596
2597 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002598 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
2599 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002600 0x4 | (ctrl->tRCD << 16) |
Angel Pons891f2bc2020-01-10 01:27:28 +01002601 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10);
Angel Pons88521882020-01-05 20:21:20 +01002602 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002603 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002604 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002605
2606 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002607 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2608 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8005020 |
Felix Held2bb3cdf2018-07-28 00:23:59 +02002609 ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002610 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002611 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002612 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002613
2614 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002615 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2616 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002617 0x4005020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002618 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002619 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002620 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002621
2622 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002623 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2624 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002625 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002626 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002627 (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002628 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002629
Angel Pons7c49cb82020-03-16 23:17:32 +01002630 /* Execute command queue */
2631 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002632
Angel Pons88521882020-01-05 20:21:20 +01002633 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002634 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002635 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002636 }
2637
Angel Pons7c49cb82020-03-16 23:17:32 +01002638 /* FIXME: This register only exists on Ivy Bridge */
2639 raw_stats[edge] = MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002640 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002641
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002642 FOR_ALL_LANES {
2643 struct run rn;
2644 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
Angel Pons7c49cb82020-03-16 23:17:32 +01002645 stats[edge] = !!(raw_stats[edge] & (1 << lane));
2646
2647 rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
2648
2649 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, "
2650 "0x%02x-0x%02x\n", channel, slotrank, i, rn.start,
2651 rn.middle, rn.end, rn.start + ctrl->edge_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002652 rn.end - ctrl->edge_offset[i]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002653
2654 lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
2655 upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
2656
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002657 edges[lane] = (lower[lane] + upper[lane]) / 2;
2658 if (rn.all || (lower[lane] > upper[lane])) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002659 printk(BIOS_EMERG, "edge write discovery failed: "
2660 "%d, %d, %d\n", channel, slotrank, lane);
2661
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002662 return MAKE_ERR;
2663 }
2664 }
2665 }
2666 }
2667
Angel Pons88521882020-01-05 20:21:20 +01002668 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002669 printram("CPA\n");
2670 return 0;
2671}
2672
2673int discover_edges_write(ramctr_timing *ctrl)
2674{
2675 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
Angel Pons7c49cb82020-03-16 23:17:32 +01002676 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2677 int channel, slotrank, lane, err;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002678
Angel Pons7c49cb82020-03-16 23:17:32 +01002679 /*
2680 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2681 * also use a single loop. It would seem that it is a debugging configuration.
2682 */
Angel Pons88521882020-01-05 20:21:20 +01002683 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2684 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
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 falling_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) = 0x200;
2694 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002695
2696 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2697 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002698 rising_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002699 if (err)
2700 return err;
2701 }
2702
Angel Pons88521882020-01-05 20:21:20 +01002703 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002704
2705 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2706 ctrl->timings[channel][slotrank].lanes[lane].falling =
Angel Pons7c49cb82020-03-16 23:17:32 +01002707 falling_edges[channel][slotrank][lane];
2708
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002709 ctrl->timings[channel][slotrank].lanes[lane].rising =
Angel Pons7c49cb82020-03-16 23:17:32 +01002710 rising_edges[channel][slotrank][lane];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002711 }
2712
2713 FOR_ALL_POPULATED_CHANNELS
2714 program_timings(ctrl, channel);
2715
2716 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002717 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002718 }
2719 return 0;
2720}
2721
2722static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2723{
Angel Pons88521882020-01-05 20:21:20 +01002724 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002725
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002726 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002727 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0001f006;
2728 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002729 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | (ctrl->tRCD << 16) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01002730 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2731 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x0244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002732
2733 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002734 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2735 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002736 0x80011e0 | ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002737 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002738 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002739
2740 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002741 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2742 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x40011e0 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002743 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002744 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002745
2746 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002747 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2748 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x1001 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002749 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002750 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002751
Angel Pons7c49cb82020-03-16 23:17:32 +01002752 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002753 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002754
Angel Pons88521882020-01-05 20:21:20 +01002755 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002756}
2757
2758int discover_timC_write(ramctr_timing *ctrl)
2759{
Angel Pons7c49cb82020-03-16 23:17:32 +01002760 const u8 rege3c_b24[3] = { 0, 0x0f, 0x2f };
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002761 int i, pat;
2762
2763 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2764 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2765 int channel, slotrank, lane;
2766
2767 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2768 lower[channel][slotrank][lane] = 0;
2769 upper[channel][slotrank][lane] = MAX_TIMC;
2770 }
2771
Angel Pons88521882020-01-05 20:21:20 +01002772 /*
2773 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2774 * FIXME: This must only be done on Ivy Bridge.
2775 */
2776 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002777 printram("discover timC write:\n");
2778
2779 for (i = 0; i < 3; i++)
2780 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002781
2782 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
2783 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel),
2784 ~0x3f000000, rege3c_b24[i] << 24);
2785
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002786 udelay(2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002787
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002788 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2789 FOR_ALL_POPULATED_RANKS {
2790 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01002791 u32 raw_stats[MAX_TIMC + 1];
2792 int stats[MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002793
2794 /* Make sure rn.start < rn.end */
Angel Pons7c49cb82020-03-16 23:17:32 +01002795 stats[MAX_TIMC] = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002796
2797 fill_pattern5(ctrl, channel, pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002798 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
2799
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002800 for (timC = 0; timC < MAX_TIMC; timC++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002801 FOR_ALL_LANES {
2802 ctrl->timings[channel][slotrank]
2803 .lanes[lane].timC = timC;
2804 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002805 program_timings(ctrl, channel);
2806
2807 test_timC_write (ctrl, channel, slotrank);
2808
Angel Pons7c49cb82020-03-16 23:17:32 +01002809 /* FIXME: Another IVB-only register! */
2810 raw_stats[timC] =
Angel Pons1aba2a32020-01-05 22:31:41 +01002811 MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002812 }
2813 FOR_ALL_LANES {
2814 struct run rn;
Angel Pons7c49cb82020-03-16 23:17:32 +01002815 for (timC = 0; timC < MAX_TIMC; timC++) {
2816 stats[timC] = !!(raw_stats[timC]
2817 & (1 << lane));
2818 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002819
Angel Pons7c49cb82020-03-16 23:17:32 +01002820 rn = get_longest_zero_run(stats, MAX_TIMC + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002821 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002822 printk(BIOS_EMERG,
2823 "timC write discovery failed: "
2824 "%d, %d, %d\n", channel,
2825 slotrank, lane);
2826
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002827 return MAKE_ERR;
2828 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002829 printram("timC: %d, %d, %d: "
2830 "0x%02x-0x%02x-0x%02x, "
2831 "0x%02x-0x%02x\n", channel, slotrank,
2832 i, rn.start, rn.middle, rn.end,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002833 rn.start + ctrl->timC_offset[i],
Angel Pons7c49cb82020-03-16 23:17:32 +01002834 rn.end - ctrl->timC_offset[i]);
2835
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002836 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002837 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002838 lower[channel][slotrank][lane]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002839
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002840 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002841 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002842 upper[channel][slotrank][lane]);
2843
2844 }
2845 }
2846 }
2847 }
2848
2849 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002850 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
Angel Pons88521882020-01-05 20:21:20 +01002851 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002852 udelay(2);
2853 }
2854
Angel Pons88521882020-01-05 20:21:20 +01002855 /*
2856 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2857 * FIXME: This must only be done on Ivy Bridge.
2858 */
2859 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002860
2861 printram("CPB\n");
2862
2863 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002864 printram("timC %d, %d, %d: %x\n", channel, slotrank, lane,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002865 (lower[channel][slotrank][lane] +
2866 upper[channel][slotrank][lane]) / 2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002867
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002868 ctrl->timings[channel][slotrank].lanes[lane].timC =
2869 (lower[channel][slotrank][lane] +
2870 upper[channel][slotrank][lane]) / 2;
2871 }
2872 FOR_ALL_POPULATED_CHANNELS {
2873 program_timings(ctrl, channel);
2874 }
2875 return 0;
2876}
2877
Angel Pons88521882020-01-05 20:21:20 +01002878void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002879{
2880 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002881 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002882
2883 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2884 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002885 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002886 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002887 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01002888 printram("normalize %d, %d, %d: mat %d\n",
2889 channel, slotrank, lane, mat);
2890
Felix Heldef4fe3e2019-12-31 14:15:05 +01002891 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01002892 printram("normalize %d, %d, %d: delta %d\n",
2893 channel, slotrank, lane, delta);
2894
Angel Pons88521882020-01-05 20:21:20 +01002895 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01002896 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002897 }
2898
2899 FOR_ALL_POPULATED_CHANNELS {
2900 program_timings(ctrl, channel);
2901 }
2902}
2903
Angel Pons88521882020-01-05 20:21:20 +01002904void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002905{
2906 int channel, slotrank;
2907
2908 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002909 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002910 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01002911 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002912 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002913 }
2914}
2915
2916int channel_test(ramctr_timing *ctrl)
2917{
2918 int channel, slotrank, lane;
2919
2920 slotrank = 0;
2921 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002922 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01002923 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002924 return MAKE_ERR;
2925 }
2926 FOR_ALL_POPULATED_CHANNELS {
2927 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
2928
Angel Pons88521882020-01-05 20:21:20 +01002929 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002930 }
2931
2932 for (slotrank = 0; slotrank < 4; slotrank++)
2933 FOR_ALL_CHANNELS
2934 if (ctrl->rankmap[channel] & (1 << slotrank)) {
2935 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002936 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
2937 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002938 }
Angel Pons88521882020-01-05 20:21:20 +01002939 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002940
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002941 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002942 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0001f006;
2943 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x0028a004;
Angel Pons891f2bc2020-01-10 01:27:28 +01002944 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x00060000 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002945 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x00000244;
Felix Held9cf1dd22018-07-31 14:52:40 +02002946
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002947 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002948 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x0001f201;
2949 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x08281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002950 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002951 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002952
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002953 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002954 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x0001f105;
2955 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x04281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002956 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002957 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002958
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002959 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002960 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x0001f002;
2961 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x00280c01;
Angel Pons891f2bc2020-01-10 01:27:28 +01002962 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = 0x00060400 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002963 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x00000240;
Felix Held9cf1dd22018-07-31 14:52:40 +02002964
Angel Pons7c49cb82020-03-16 23:17:32 +01002965 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002966 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002967
Angel Pons88521882020-01-05 20:21:20 +01002968 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002969 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01002970 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002971 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
2972 channel, slotrank, lane);
2973 return MAKE_ERR;
2974 }
2975 }
2976 return 0;
2977}
2978
Angel Pons88521882020-01-05 20:21:20 +01002979void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002980{
2981 int channel;
2982
Angel Pons7c49cb82020-03-16 23:17:32 +01002983 /* 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 +01002984 static u32 seeds[NUM_CHANNELS][3] = {
2985 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
2986 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
2987 };
2988 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002989 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002990 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
2991 MCHBAR32(SCRAMBLING_SEED_2_HI_ch(channel)) = seeds[channel][1];
2992 MCHBAR32(SCRAMBLING_SEED_2_LO_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002993 }
2994}
2995
Angel Pons7c49cb82020-03-16 23:17:32 +01002996void set_wmm_behavior(void)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002997{
Angel Pons7c49cb82020-03-16 23:17:32 +01002998 u32 cpu = cpu_get_cpuid();
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002999
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003000 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003001 MCHBAR32(SC_WDBWM) = 0x141d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003002 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01003003 MCHBAR32(SC_WDBWM) = 0x551d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003004 }
3005}
3006
Angel Pons88521882020-01-05 20:21:20 +01003007void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003008{
3009 int channel;
3010
3011 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003012 /* Always drive command bus */
Angel Pons88521882020-01-05 20:21:20 +01003013 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003014 }
3015
3016 udelay(1);
3017
3018 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003019 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003020 }
3021}
3022
Angel Pons7c49cb82020-03-16 23:17:32 +01003023void set_read_write_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003024{
3025 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003026
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003027 FOR_ALL_POPULATED_CHANNELS {
3028 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003029 int min_pi = 10000;
3030 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003031
3032 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003033 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3034 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003035 }
3036
Angel Pons7c49cb82020-03-16 23:17:32 +01003037 b20 = (max_pi - min_pi > 51) ? 0 : ctrl->ref_card_offset[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003038
Angel Pons7c49cb82020-03-16 23:17:32 +01003039 b4_8_12 = (ctrl->pi_coding_threshold < max_pi - min_pi) ? 0x3330 : 0x2220;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003040
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003041 dram_odt_stretch(ctrl, channel);
3042
Angel Pons7c49cb82020-03-16 23:17:32 +01003043 MCHBAR32(TC_RWP_ch(channel)) = 0x0a000000 | (b20 << 20) |
Felix Held2463aa92018-07-29 21:37:55 +02003044 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003045 }
3046}
3047
Angel Pons88521882020-01-05 20:21:20 +01003048void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003049{
3050 int channel;
3051 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003052 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3053 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003054 }
3055}
3056
Angel Pons7c49cb82020-03-16 23:17:32 +01003057/* Encode the watermark latencies in a suitable format for graphics drivers consumption */
3058static int encode_wm(int ns)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003059{
Angel Pons88521882020-01-05 20:21:20 +01003060 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003061}
3062
Angel Pons7c49cb82020-03-16 23:17:32 +01003063/* FIXME: values in this function should be hardware revision-dependent */
Angel Pons88521882020-01-05 20:21:20 +01003064void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003065{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003066 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3067
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003068 int channel;
3069 int t1_cycles = 0, t1_ns = 0, t2_ns;
3070 int t3_ns;
3071 u32 r32;
3072
Angel Pons7c49cb82020-03-16 23:17:32 +01003073 /* FIXME: This register only exists on Ivy Bridge */
3074 MCHBAR32(WMM_READ_CONFIG) = 0x46;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003075
Felix Heldf9b826a2018-07-30 17:56:52 +02003076 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003077 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xffffcfff, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003078
Patrick Rudolph74203de2017-11-20 11:57:01 +01003079 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003080 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003081 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003082 else
Angel Pons7c49cb82020-03-16 23:17:32 +01003083 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003084 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003085
Felix Heldf9b826a2018-07-30 17:56:52 +02003086 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003087 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003088
Angel Pons88521882020-01-05 20:21:20 +01003089 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3090 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003091
3092 FOR_ALL_CHANNELS {
3093 switch (ctrl->rankmap[channel]) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003094 /* Unpopulated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003095 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003096 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003097 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003098 /* Only single-ranked dimms */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003099 case 1:
3100 case 4:
3101 case 5:
Angel Pons7c49cb82020-03-16 23:17:32 +01003102 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x00373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003103 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003104 /* Dual-ranked dimms present */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003105 default:
Angel Pons7c49cb82020-03-16 23:17:32 +01003106 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x009b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003107 break;
3108 }
3109 }
3110
Felix Held50b7ed22019-12-30 20:41:54 +01003111 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
Angel Pons7c49cb82020-03-16 23:17:32 +01003112 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0x00ffffff, 0x00e4d5d0);
Felix Held50b7ed22019-12-30 20:41:54 +01003113 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003114
3115 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003116 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~(3 << 16), 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003117
Angel Pons88521882020-01-05 20:21:20 +01003118 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3119 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3120 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003121
Angel Pons7c49cb82020-03-16 23:17:32 +01003122 /* Find a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003123 FOR_ALL_POPULATED_CHANNELS
3124 break;
3125
Angel Pons88521882020-01-05 20:21:20 +01003126 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3127 r32 = MCHBAR32(PM_DLL_CONFIG);
Angel Pons7c49cb82020-03-16 23:17:32 +01003128 if (r32 & (1 << 17))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003129 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003130 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003131 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
Angel Pons7c49cb82020-03-16 23:17:32 +01003132 if (!(r32 & (1 << 17)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003133 t1_ns += 500;
3134
Angel Pons88521882020-01-05 20:21:20 +01003135 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003136 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003137 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003138 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003139 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003140 t3_ns = 500;
3141 }
Angel Pons7c49cb82020-03-16 23:17:32 +01003142
3143 /* The graphics driver will use these watermark values */
3144 printk(BIOS_DEBUG, "t123: %d, %d, %d\n", t1_ns, t2_ns, t3_ns);
3145 MCHBAR32_AND_OR(SSKPD, 0xC0C0C0C0,
3146 ((encode_wm(t1_ns) + encode_wm(t2_ns)) << 16) | (encode_wm(t1_ns) << 8) |
3147 ((encode_wm(t3_ns) + encode_wm(t2_ns) + encode_wm(t1_ns)) << 24) | 0x0c);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003148}
3149
Angel Pons88521882020-01-05 20:21:20 +01003150void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003151{
3152 int channel, slotrank, lane;
3153
Angel Pons7c49cb82020-03-16 23:17:32 +01003154 FOR_ALL_POPULATED_CHANNELS {
3155 MCHBAR32(TC_RAP_ch(channel)) =
3156 (ctrl->tRRD << 0)
3157 | (ctrl->tRTP << 4)
3158 | (ctrl->tCKE << 8)
3159 | (ctrl->tWTR << 12)
3160 | (ctrl->tFAW << 16)
3161 | (ctrl->tWR << 24)
3162 | (ctrl->cmd_stretch[channel] << 30);
3163 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003164
3165 udelay(1);
3166
3167 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003168 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003169 }
3170
3171 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003172 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003173 }
3174
3175 FOR_ALL_POPULATED_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003176 MCHBAR32_OR(TC_RWP_ch(channel), 0x08000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003177
3178 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003179 udelay(1);
3180 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x00200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003181 }
3182
3183 printram("CPE\n");
3184
Angel Pons88521882020-01-05 20:21:20 +01003185 MCHBAR32(GDCRTRAININGMOD) = 0;
3186 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003187
3188 printram("CP5b\n");
3189
3190 FOR_ALL_POPULATED_CHANNELS {
3191 program_timings(ctrl, channel);
3192 }
3193
3194 u32 reg, addr;
3195
Angel Pons7c49cb82020-03-16 23:17:32 +01003196 /* Poll for RCOMP */
3197 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
3198 ;
3199
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003200 do {
Angel Pons88521882020-01-05 20:21:20 +01003201 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003202 } while ((reg & 0x14) == 0);
3203
Angel Pons7c49cb82020-03-16 23:17:32 +01003204 /* Set state of memory controller */
Angel Pons88521882020-01-05 20:21:20 +01003205 MCHBAR32(MC_INIT_STATE_G) = 0x116;
Angel Pons7c49cb82020-03-16 23:17:32 +01003206 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003207
Angel Pons7c49cb82020-03-16 23:17:32 +01003208 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003209 udelay(500);
3210
3211 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003212 /* Set valid rank CKE */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003213 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01003214 reg = (reg & ~0x0f) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003215 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003216 MCHBAR32(addr) = reg;
3217
Angel Pons7c49cb82020-03-16 23:17:32 +01003218 /* Wait 10ns for ranks to settle */
3219 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003220
3221 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3222 MCHBAR32(addr) = reg;
3223
Angel Pons7c49cb82020-03-16 23:17:32 +01003224 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003225 write_reset(ctrl);
3226 }
3227
Angel Pons7c49cb82020-03-16 23:17:32 +01003228 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003229 dram_mrscommands(ctrl);
3230
3231 printram("CP5c\n");
3232
Angel Pons88521882020-01-05 20:21:20 +01003233 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003234
3235 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003236 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003237 udelay(2);
3238 }
3239
Angel Pons88521882020-01-05 20:21:20 +01003240 /*
3241 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
3242 * FIXME: This must only be done on Ivy Bridge. Moreover, this instance seems to be
3243 * spurious, because nothing else enabled this optimization before.
3244 */
3245 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003246}