blob: 9eb60c74c91bab9955e19cf127e7348ead0e6c14 [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"
Angel Pons7f6586f2020-03-21 12:45:12 +010027#include "raminit_tables.h"
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010028#include "sandybridge.h"
29
Angel Pons7c49cb82020-03-16 23:17:32 +010030/* FIXME: no ECC support */
31/* FIXME: no support for 3-channel chipsets */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010032
Angel Pons88521882020-01-05 20:21:20 +010033/* length: [1..4] */
34#define IOSAV_RUN_ONCE(length) ((((length) - 1) << 18) | 1)
Felix Held9cf1dd22018-07-31 14:52:40 +020035
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010036static void sfence(void)
37{
38 asm volatile ("sfence");
39}
40
Angel Pons7c49cb82020-03-16 23:17:32 +010041/* Toggle IO reset bit */
42static void toggle_io_reset(void)
43{
Angel Pons88521882020-01-05 20:21:20 +010044 u32 r32 = MCHBAR32(MC_INIT_STATE_G);
Angel Pons7c49cb82020-03-16 23:17:32 +010045 MCHBAR32(MC_INIT_STATE_G) = r32 | 0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010046 udelay(1);
Angel Pons88521882020-01-05 20:21:20 +010047 MCHBAR32(MC_INIT_STATE_G) = r32 & ~0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010048 udelay(1);
49}
50
51static u32 get_XOVER_CLK(u8 rankmap)
52{
53 return rankmap << 24;
54}
55
56static u32 get_XOVER_CMD(u8 rankmap)
57{
58 u32 reg;
59
Angel Pons7c49cb82020-03-16 23:17:32 +010060 /* Enable xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010061 reg = 0x4000;
62
Angel Pons7c49cb82020-03-16 23:17:32 +010063 /* Enable xover ctl */
64 if (rankmap & 0x03)
65 reg |= (1 << 17);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010066
Angel Pons7c49cb82020-03-16 23:17:32 +010067 if (rankmap & 0x0c)
68 reg |= (1 << 26);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010069
70 return reg;
71}
72
Angel Pons7c49cb82020-03-16 23:17:32 +010073/* CAS write latency. To be programmed in MR2. See DDR3 SPEC for MR2 documentation. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010074u8 get_CWL(u32 tCK)
75{
Angel Pons7c49cb82020-03-16 23:17:32 +010076 /* Get CWL based on tCK using the following rule */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010077 switch (tCK) {
78 case TCK_1333MHZ:
79 return 12;
Angel Pons7c49cb82020-03-16 23:17:32 +010080
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010081 case TCK_1200MHZ:
82 case TCK_1100MHZ:
83 return 11;
Angel Pons7c49cb82020-03-16 23:17:32 +010084
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010085 case TCK_1066MHZ:
86 case TCK_1000MHZ:
87 return 10;
Angel Pons7c49cb82020-03-16 23:17:32 +010088
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010089 case TCK_933MHZ:
90 case TCK_900MHZ:
91 return 9;
Angel Pons7c49cb82020-03-16 23:17:32 +010092
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010093 case TCK_800MHZ:
94 case TCK_700MHZ:
95 return 8;
Angel Pons7c49cb82020-03-16 23:17:32 +010096
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010097 case TCK_666MHZ:
98 return 7;
Angel Pons7c49cb82020-03-16 23:17:32 +010099
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100100 case TCK_533MHZ:
101 return 6;
Angel Pons7c49cb82020-03-16 23:17:32 +0100102
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100103 default:
104 return 5;
105 }
106}
107
108void dram_find_common_params(ramctr_timing *ctrl)
109{
110 size_t valid_dimms;
111 int channel, slot;
112 dimm_info *dimms = &ctrl->info;
113
114 ctrl->cas_supported = (1 << (MAX_CAS - MIN_CAS + 1)) - 1;
115 valid_dimms = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100116
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100117 FOR_ALL_CHANNELS for (slot = 0; slot < 2; slot++) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100118
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100119 const dimm_attr *dimm = &dimms->dimm[channel][slot];
120 if (dimm->dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3)
121 continue;
Angel Pons7c49cb82020-03-16 23:17:32 +0100122
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100123 valid_dimms++;
124
125 /* Find all possible CAS combinations */
126 ctrl->cas_supported &= dimm->cas_supported;
127
128 /* Find the smallest common latencies supported by all DIMMs */
Angel Pons7c49cb82020-03-16 23:17:32 +0100129 ctrl->tCK = MAX(ctrl->tCK, dimm->tCK);
130 ctrl->tAA = MAX(ctrl->tAA, dimm->tAA);
131 ctrl->tWR = MAX(ctrl->tWR, dimm->tWR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100132 ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD);
133 ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD);
Angel Pons7c49cb82020-03-16 23:17:32 +0100134 ctrl->tRP = MAX(ctrl->tRP, dimm->tRP);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100135 ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS);
136 ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC);
137 ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR);
138 ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP);
139 ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW);
Dan Elkoubydabebc32018-04-13 18:47:10 +0300140 ctrl->tCWL = MAX(ctrl->tCWL, dimm->tCWL);
141 ctrl->tCMD = MAX(ctrl->tCMD, dimm->tCMD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100142 }
143
144 if (!ctrl->cas_supported)
Angel Pons7c49cb82020-03-16 23:17:32 +0100145 die("Unsupported DIMM combination. DIMMS do not support common CAS latency");
146
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100147 if (!valid_dimms)
148 die("No valid DIMMs found");
149}
150
Angel Pons88521882020-01-05 20:21:20 +0100151void dram_xover(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100152{
153 u32 reg;
154 int channel;
155
156 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100157 /* Enable xover clk */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100158 reg = get_XOVER_CLK(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100159 printram("XOVER CLK [%x] = %x\n", GDCRCKPICODE_ch(channel), reg);
160 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100161
Angel Pons7c49cb82020-03-16 23:17:32 +0100162 /* Enable xover ctl & xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100163 reg = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100164 printram("XOVER CMD [%x] = %x\n", GDCRCMDPICODING_ch(channel), reg);
165 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100166 }
167}
168
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100169static void dram_odt_stretch(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100170{
Iru Cai89af71c2018-08-16 16:46:27 +0800171 u32 addr, cpu, stretch;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100172
173 stretch = ctrl->ref_card_offset[channel];
Angel Pons7c49cb82020-03-16 23:17:32 +0100174 /*
175 * ODT stretch:
176 * Delay ODT signal by stretch value. Useful for multi DIMM setups on the same channel.
177 */
Subrata Banik53b08c32018-12-10 14:11:35 +0530178 cpu = cpu_get_cpuid();
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100179 if (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_C(cpu)) {
180 if (stretch == 2)
181 stretch = 3;
Angel Pons7c49cb82020-03-16 23:17:32 +0100182
Angel Pons88521882020-01-05 20:21:20 +0100183 addr = SCHED_SECOND_CBIT_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100184 MCHBAR32_AND_OR(addr, 0xffffc3ff, (stretch << 12) | (stretch << 10));
185 printk(RAM_DEBUG, "OTHP Workaround [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100186 } else {
Angel Pons88521882020-01-05 20:21:20 +0100187 addr = TC_OTHP_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100188 MCHBAR32_AND_OR(addr, 0xfff0ffff, (stretch << 16) | (stretch << 18));
Iru Cai89af71c2018-08-16 16:46:27 +0800189 printk(RAM_DEBUG, "OTHP [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100190 }
191}
192
193void dram_timing_regs(ramctr_timing *ctrl)
194{
195 u32 reg, addr, val32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100196 int channel;
197
198 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100199 /* BIN parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100200 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100201 reg |= (ctrl->tRCD << 0);
202 reg |= (ctrl->tRP << 4);
203 reg |= (ctrl->CAS << 8);
204 reg |= (ctrl->CWL << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100205 reg |= (ctrl->tRAS << 16);
Angel Pons88521882020-01-05 20:21:20 +0100206 printram("DBP [%x] = %x\n", TC_DBP_ch(channel), reg);
207 MCHBAR32(TC_DBP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100208
Angel Pons7c49cb82020-03-16 23:17:32 +0100209 /* Regular access parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100210 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100211 reg |= (ctrl->tRRD << 0);
212 reg |= (ctrl->tRTP << 4);
213 reg |= (ctrl->tCKE << 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100214 reg |= (ctrl->tWTR << 12);
215 reg |= (ctrl->tFAW << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +0100216 reg |= (ctrl->tWR << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100217 reg |= (3 << 30);
Angel Pons88521882020-01-05 20:21:20 +0100218 printram("RAP [%x] = %x\n", TC_RAP_ch(channel), reg);
219 MCHBAR32(TC_RAP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100220
Angel Pons7c49cb82020-03-16 23:17:32 +0100221 /* Other parameters */
Angel Pons88521882020-01-05 20:21:20 +0100222 addr = TC_OTHP_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100223 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100224 reg |= (ctrl->tXPDLL << 0);
225 reg |= (ctrl->tXP << 5);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100226 reg |= (ctrl->tAONPD << 8);
227 reg |= 0xa0000;
228 printram("OTHP [%x] = %x\n", addr, reg);
229 MCHBAR32(addr) = reg;
230
Angel Pons7c49cb82020-03-16 23:17:32 +0100231 /* FIXME: This register might as well not exist */
Angel Pons1aba2a32020-01-05 22:31:41 +0100232 MCHBAR32(0x4014 + channel * 0x400) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100233
Felix Held9fe248f2018-07-31 20:59:45 +0200234 MCHBAR32_OR(addr, 0x00020000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100235
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100236 dram_odt_stretch(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100237
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100238 /*
Angel Pons7c49cb82020-03-16 23:17:32 +0100239 * TC-Refresh timing parameters:
240 * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow
241 * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024.
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100242 */
243 val32 = MIN((ctrl->tREFI * 89) / 10, (70000 << 8) / ctrl->tCK);
244
Angel Pons7c49cb82020-03-16 23:17:32 +0100245 reg = ((ctrl->tREFI & 0xffff) << 0) |
246 ((ctrl->tRFC & 0x01ff) << 16) | (((val32 / 1024) & 0x7f) << 25);
247
Angel Pons88521882020-01-05 20:21:20 +0100248 printram("REFI [%x] = %x\n", TC_RFTP_ch(channel), reg);
249 MCHBAR32(TC_RFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100250
Angel Pons88521882020-01-05 20:21:20 +0100251 MCHBAR32_OR(TC_RFP_ch(channel), 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100252
Angel Pons7c49cb82020-03-16 23:17:32 +0100253 /* Self-refresh timing parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100254 reg = 0;
255 val32 = tDLLK;
Angel Pons7c49cb82020-03-16 23:17:32 +0100256 reg = (reg & ~0x00000fff) | (val32 << 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100257 val32 = ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100258 reg = (reg & ~0x0000f000) | (val32 << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100259 val32 = tDLLK - ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100260 reg = (reg & ~0x03ff0000) | (val32 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100261 val32 = ctrl->tMOD - 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100262 reg = (reg & ~0xf0000000) | (val32 << 28);
263 printram("SRFTP [%x] = %x\n", TC_SRFTP_ch(channel), reg);
Angel Pons88521882020-01-05 20:21:20 +0100264 MCHBAR32(TC_SRFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100265 }
266}
267
268void dram_dimm_mapping(ramctr_timing *ctrl)
269{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100270 int channel;
271 dimm_info *info = &ctrl->info;
272
273 FOR_ALL_CHANNELS {
Nico Huberac4f2162017-10-01 18:14:43 +0200274 dimm_attr *dimmA, *dimmB;
275 u32 reg = 0;
276
Angel Pons7c49cb82020-03-16 23:17:32 +0100277 if (info->dimm[channel][0].size_mb >= info->dimm[channel][1].size_mb) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100278 dimmA = &info->dimm[channel][0];
279 dimmB = &info->dimm[channel][1];
Angel Pons7c49cb82020-03-16 23:17:32 +0100280 reg |= (0 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100281 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100282 dimmA = &info->dimm[channel][1];
283 dimmB = &info->dimm[channel][0];
Angel Pons7c49cb82020-03-16 23:17:32 +0100284 reg |= (1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100285 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100286
Nico Huberac4f2162017-10-01 18:14:43 +0200287 if (dimmA && (dimmA->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100288 reg |= (dimmA->size_mb / 256) << 0;
289 reg |= (dimmA->ranks - 1) << 17;
Nico Huberac4f2162017-10-01 18:14:43 +0200290 reg |= (dimmA->width / 8 - 1) << 19;
291 }
292
293 if (dimmB && (dimmB->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100294 reg |= (dimmB->size_mb / 256) << 8;
295 reg |= (dimmB->ranks - 1) << 18;
Nico Huberac4f2162017-10-01 18:14:43 +0200296 reg |= (dimmB->width / 8 - 1) << 20;
297 }
298
Angel Pons7c49cb82020-03-16 23:17:32 +0100299 reg |= 1 << 21; /* Rank interleave */
300 reg |= 1 << 22; /* Enhanced interleave */
Nico Huberac4f2162017-10-01 18:14:43 +0200301
Angel Pons7c49cb82020-03-16 23:17:32 +0100302 if ((dimmA && (dimmA->ranks > 0)) || (dimmB && (dimmB->ranks > 0))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100303 ctrl->mad_dimm[channel] = reg;
304 } else {
305 ctrl->mad_dimm[channel] = 0;
306 }
307 }
308}
309
Angel Pons88521882020-01-05 20:21:20 +0100310void dram_dimm_set_mapping(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100311{
312 int channel;
313 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100314 MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100315 }
316}
317
Angel Pons88521882020-01-05 20:21:20 +0100318void dram_zones(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100319{
320 u32 reg, ch0size, ch1size;
321 u8 val;
322 reg = 0;
323 val = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100324
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100325 if (training) {
326 ch0size = ctrl->channel_size_mb[0] ? 256 : 0;
327 ch1size = ctrl->channel_size_mb[1] ? 256 : 0;
328 } else {
329 ch0size = ctrl->channel_size_mb[0];
330 ch1size = ctrl->channel_size_mb[1];
331 }
332
333 if (ch0size >= ch1size) {
Angel Pons88521882020-01-05 20:21:20 +0100334 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100335 val = ch1size / 256;
336 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100337 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100338 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100339 MCHBAR32(MAD_CHNL) = 0x24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100340
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100341 } else {
Angel Pons88521882020-01-05 20:21:20 +0100342 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100343 val = ch0size / 256;
344 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100345 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100346 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100347 MCHBAR32(MAD_CHNL) = 0x21;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100348 }
349}
350
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100351#define DEFAULT_TCK TCK_800MHZ
352
353unsigned int get_mem_min_tck(void)
354{
355 u32 reg32;
356 u8 rev;
357 const struct device *dev;
358 const struct northbridge_intel_sandybridge_config *cfg = NULL;
359
Angel Ponsb31d1d72020-01-10 01:35:09 +0100360 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100361 if (dev)
362 cfg = dev->chip_info;
363
364 /* If this is zero, it just means devicetree.cb didn't set it */
365 if (!cfg || cfg->max_mem_clock_mhz == 0) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100366
Julius Wernercd49cce2019-03-05 16:53:33 -0800367 if (CONFIG(NATIVE_RAMINIT_IGNORE_MAX_MEM_FUSES))
Patrick Rudolphb794a692017-08-08 13:13:51 +0200368 return TCK_1333MHZ;
369
Angel Ponsb31d1d72020-01-10 01:35:09 +0100370 rev = pci_read_config8(HOST_BRIDGE, PCI_DEVICE_ID);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100371
372 if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100373 /* Read Capabilities A Register DMFC bits */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100374 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100375 reg32 &= 0x7;
376
377 switch (reg32) {
378 case 7: return TCK_533MHZ;
379 case 6: return TCK_666MHZ;
380 case 5: return TCK_800MHZ;
Angel Pons7c49cb82020-03-16 23:17:32 +0100381 /* Reserved */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100382 default:
383 break;
384 }
385 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +0100386 /* Read Capabilities B Register DMFC bits */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100387 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_B);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100388 reg32 = (reg32 >> 4) & 0x7;
389
390 switch (reg32) {
391 case 7: return TCK_533MHZ;
392 case 6: return TCK_666MHZ;
393 case 5: return TCK_800MHZ;
394 case 4: return TCK_933MHZ;
395 case 3: return TCK_1066MHZ;
396 case 2: return TCK_1200MHZ;
397 case 1: return TCK_1333MHZ;
Angel Pons7c49cb82020-03-16 23:17:32 +0100398 /* Reserved */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100399 default:
400 break;
401 }
402 }
403 return DEFAULT_TCK;
404 } else {
405 if (cfg->max_mem_clock_mhz >= 1066)
406 return TCK_1066MHZ;
407 else if (cfg->max_mem_clock_mhz >= 933)
408 return TCK_933MHZ;
409 else if (cfg->max_mem_clock_mhz >= 800)
410 return TCK_800MHZ;
411 else if (cfg->max_mem_clock_mhz >= 666)
412 return TCK_666MHZ;
413 else if (cfg->max_mem_clock_mhz >= 533)
414 return TCK_533MHZ;
415 else
416 return TCK_400MHZ;
417 }
418}
419
420#define DEFAULT_PCI_MMIO_SIZE 2048
421
422static unsigned int get_mmio_size(void)
423{
424 const struct device *dev;
425 const struct northbridge_intel_sandybridge_config *cfg = NULL;
426
Angel Ponsb31d1d72020-01-10 01:35:09 +0100427 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100428 if (dev)
429 cfg = dev->chip_info;
430
431 /* If this is zero, it just means devicetree.cb didn't set it */
432 if (!cfg || cfg->pci_mmio_size == 0)
433 return DEFAULT_PCI_MMIO_SIZE;
434 else
435 return cfg->pci_mmio_size;
436}
437
Angel Pons88521882020-01-05 20:21:20 +0100438void dram_memorymap(ramctr_timing *ctrl, int me_uma_size)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100439{
Angel Pons7c49cb82020-03-16 23:17:32 +0100440 u32 reg, val, reclaim, tom, gfxstolen, gttsize;
441 size_t tsegbase, toludbase, remapbase, gfxstolenbase, mmiosize, gttbase;
442 size_t tsegsize, touudbase, remaplimit, mestolenbase, tsegbasedelta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100443 uint16_t ggc;
444
445 mmiosize = get_mmio_size();
446
Felix Held87ddea22020-01-26 04:55:27 +0100447 ggc = pci_read_config16(HOST_BRIDGE, GGC);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100448 if (!(ggc & 2)) {
449 gfxstolen = ((ggc >> 3) & 0x1f) * 32;
Angel Pons7c49cb82020-03-16 23:17:32 +0100450 gttsize = ((ggc >> 8) & 0x3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100451 } else {
452 gfxstolen = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100453 gttsize = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100454 }
455
456 tsegsize = CONFIG_SMM_TSEG_SIZE >> 20;
457
458 tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1];
459
460 mestolenbase = tom - me_uma_size;
461
Angel Pons7c49cb82020-03-16 23:17:32 +0100462 toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize, tom - me_uma_size);
463
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100464 gfxstolenbase = toludbase - gfxstolen;
465 gttbase = gfxstolenbase - gttsize;
466
467 tsegbase = gttbase - tsegsize;
468
Angel Pons7c49cb82020-03-16 23:17:32 +0100469 /* Round tsegbase down to nearest address aligned to tsegsize */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100470 tsegbasedelta = tsegbase & (tsegsize - 1);
471 tsegbase &= ~(tsegsize - 1);
472
473 gttbase -= tsegbasedelta;
474 gfxstolenbase -= tsegbasedelta;
475 toludbase -= tsegbasedelta;
476
Angel Pons7c49cb82020-03-16 23:17:32 +0100477 /* Test if it is possible to reclaim a hole in the RAM addressing */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100478 if (tom - me_uma_size > toludbase) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100479 /* Reclaim is possible */
480 reclaim = 1;
481 remapbase = MAX(4096, tom - me_uma_size);
482 remaplimit = remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1;
483 touudbase = remaplimit + 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100484 } else {
485 // Reclaim not possible
Angel Pons7c49cb82020-03-16 23:17:32 +0100486 reclaim = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100487 touudbase = tom - me_uma_size;
488 }
489
Angel Pons7c49cb82020-03-16 23:17:32 +0100490 /* Update memory map in PCIe configuration space */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100491 printk(BIOS_DEBUG, "Update PCI-E configuration space:\n");
492
Angel Pons7c49cb82020-03-16 23:17:32 +0100493 /* TOM (top of memory) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100494 reg = pci_read_config32(HOST_BRIDGE, TOM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100495 val = tom & 0xfff;
496 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100497 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100498 pci_write_config32(HOST_BRIDGE, TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100499
Angel Ponsb31d1d72020-01-10 01:35:09 +0100500 reg = pci_read_config32(HOST_BRIDGE, TOM + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100501 val = tom & 0xfffff000;
502 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100503 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100504 pci_write_config32(HOST_BRIDGE, TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100505
Angel Pons7c49cb82020-03-16 23:17:32 +0100506 /* TOLUD (Top Of Low Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100507 reg = pci_read_config32(HOST_BRIDGE, TOLUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100508 val = toludbase & 0xfff;
509 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100510 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOLUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100511 pci_write_config32(HOST_BRIDGE, TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100512
Angel Pons7c49cb82020-03-16 23:17:32 +0100513 /* TOUUD LSB (Top Of Upper Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100514 reg = pci_read_config32(HOST_BRIDGE, TOUUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100515 val = touudbase & 0xfff;
516 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100517 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100518 pci_write_config32(HOST_BRIDGE, TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100519
Angel Pons7c49cb82020-03-16 23:17:32 +0100520 /* TOUUD MSB */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100521 reg = pci_read_config32(HOST_BRIDGE, TOUUD + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100522 val = touudbase & 0xfffff000;
523 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100524 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100525 pci_write_config32(HOST_BRIDGE, TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100526
527 if (reclaim) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100528 /* REMAP BASE */
529 pci_write_config32(HOST_BRIDGE, REMAPBASE, remapbase << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100530 pci_write_config32(HOST_BRIDGE, REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100531
Angel Pons7c49cb82020-03-16 23:17:32 +0100532 /* REMAP LIMIT */
533 pci_write_config32(HOST_BRIDGE, REMAPLIMIT, remaplimit << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100534 pci_write_config32(HOST_BRIDGE, REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100535 }
Angel Pons7c49cb82020-03-16 23:17:32 +0100536 /* TSEG */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100537 reg = pci_read_config32(HOST_BRIDGE, TSEGMB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100538 val = tsegbase & 0xfff;
539 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100540 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TSEGMB, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100541 pci_write_config32(HOST_BRIDGE, TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100542
Angel Pons7c49cb82020-03-16 23:17:32 +0100543 /* GFX stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100544 reg = pci_read_config32(HOST_BRIDGE, BDSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100545 val = gfxstolenbase & 0xfff;
546 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100547 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BDSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100548 pci_write_config32(HOST_BRIDGE, BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100549
Angel Pons7c49cb82020-03-16 23:17:32 +0100550 /* GTT stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100551 reg = pci_read_config32(HOST_BRIDGE, BGSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100552 val = gttbase & 0xfff;
553 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100554 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BGSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100555 pci_write_config32(HOST_BRIDGE, BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100556
557 if (me_uma_size) {
Angel Ponsb31d1d72020-01-10 01:35:09 +0100558 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100559 val = (0x80000 - me_uma_size) & 0xfffff000;
560 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100561 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100562 pci_write_config32(HOST_BRIDGE, MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100563
Angel Pons7c49cb82020-03-16 23:17:32 +0100564 /* ME base */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100565 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100566 val = mestolenbase & 0xfff;
567 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held651f99f2019-12-30 16:28:48 +0100568 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100569 pci_write_config32(HOST_BRIDGE, MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100570
Angel Ponsb31d1d72020-01-10 01:35:09 +0100571 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100572 val = mestolenbase & 0xfffff000;
573 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100574 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100575 pci_write_config32(HOST_BRIDGE, MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100576
Angel Pons7c49cb82020-03-16 23:17:32 +0100577 /* ME mask */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100578 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100579 val = (0x80000 - me_uma_size) & 0xfff;
580 reg = (reg & ~0xfff00000) | (val << 20);
Angel Pons7c49cb82020-03-16 23:17:32 +0100581 reg = reg | ME_STLEN_EN; /* Set ME memory enable */
582 reg = reg | MELCK; /* Set lock bit on ME mem */
Felix Held651f99f2019-12-30 16:28:48 +0100583 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100584 pci_write_config32(HOST_BRIDGE, MESEG_MASK, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100585 }
586}
587
Angel Pons88521882020-01-05 20:21:20 +0100588static void wait_for_iosav(int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100589{
590 while (1) {
Angel Pons88521882020-01-05 20:21:20 +0100591 if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100592 return;
593 }
594}
595
Angel Pons88521882020-01-05 20:21:20 +0100596static void write_reset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100597{
598 int channel, slotrank;
599
Angel Pons7c49cb82020-03-16 23:17:32 +0100600 /* Choose a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100601 channel = (ctrl->rankmap[0]) ? 0 : 1;
602
Angel Pons88521882020-01-05 20:21:20 +0100603 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100604
Angel Pons7c49cb82020-03-16 23:17:32 +0100605 /* Choose a populated rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100606 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
607
608 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100609 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
610 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x80c01;
Angel Pons88521882020-01-05 20:21:20 +0100611 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100612 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100613
Angel Pons7c49cb82020-03-16 23:17:32 +0100614 /*
615 * Execute command queue - why is bit 22 set here?!
616 *
617 * This is actually using the IOSAV state machine as a timer, so refresh is allowed.
618 */
Angel Pons88521882020-01-05 20:21:20 +0100619 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = (1 << 22) | IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +0200620
Angel Pons88521882020-01-05 20:21:20 +0100621 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100622}
623
Angel Pons88521882020-01-05 20:21:20 +0100624void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100625{
Felix Held9fe248f2018-07-31 20:59:45 +0200626 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100627 int channel;
628
Angel Pons7c49cb82020-03-16 23:17:32 +0100629 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
630 ;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100631 do {
Angel Pons88521882020-01-05 20:21:20 +0100632 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100633 } while ((reg & 0x14) == 0);
634
Angel Pons7c49cb82020-03-16 23:17:32 +0100635 /* Set state of memory controller */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100636 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100637 MCHBAR32(MC_INIT_STATE_G) = reg;
638 MCHBAR32(MC_INIT_STATE) = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100639 reg |= 2; /* DDR reset */
Angel Pons88521882020-01-05 20:21:20 +0100640 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100641
Angel Pons7c49cb82020-03-16 23:17:32 +0100642 /* Assert DIMM reset signal */
643 MCHBAR32_AND(MC_INIT_STATE_G, ~2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100644
Angel Pons7c49cb82020-03-16 23:17:32 +0100645 /* Wait 200us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100646 udelay(200);
647
Angel Pons7c49cb82020-03-16 23:17:32 +0100648 /* Deassert DIMM reset signal */
Angel Pons88521882020-01-05 20:21:20 +0100649 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100650
Angel Pons7c49cb82020-03-16 23:17:32 +0100651 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100652 udelay(500);
653
Angel Pons7c49cb82020-03-16 23:17:32 +0100654 /* Enable DCLK */
Angel Pons88521882020-01-05 20:21:20 +0100655 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100656
Angel Pons7c49cb82020-03-16 23:17:32 +0100657 /* XXX Wait 20ns */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100658 udelay(1);
659
660 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100661 /* Set valid rank CKE */
Felix Held9fe248f2018-07-31 20:59:45 +0200662 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100663 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100664
Angel Pons7c49cb82020-03-16 23:17:32 +0100665 /* Wait 10ns for ranks to settle */
666 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100667
668 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100669 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100670
Angel Pons7c49cb82020-03-16 23:17:32 +0100671 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100672 write_reset(ctrl);
673 }
674}
675
676static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
677{
Angel Pons7c49cb82020-03-16 23:17:32 +0100678 /* Get ODT based on rankmap */
679 int dimms_per_ch = (ctrl->rankmap[channel] & 1) + ((ctrl->rankmap[channel] >> 2) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100680
681 if (dimms_per_ch == 1) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100682 return (const odtmap){60, 60};
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100683 } else {
684 return (const odtmap){120, 30};
685 }
686}
687
Angel Pons7c49cb82020-03-16 23:17:32 +0100688static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank, int reg, u32 val)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100689{
Angel Pons88521882020-01-05 20:21:20 +0100690 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100691
692 if (ctrl->rank_mirror[channel][slotrank]) {
693 /* DDR3 Rank1 Address mirror
Angel Pons7c49cb82020-03-16 23:17:32 +0100694 swap the following pins:
695 A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100696 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
Angel Pons7c49cb82020-03-16 23:17:32 +0100697 val = (val & ~0x1f8) | ((val >> 1) & 0xa8) | ((val & 0xa8) << 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100698 }
699
700 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100701 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f000;
702 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100703 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200704 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100705 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100706
707 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100708 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f000;
709 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100710 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200711 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100712 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100713
714 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100715 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x0f000;
716 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +0100717 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200718 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100719 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200720
Angel Pons7c49cb82020-03-16 23:17:32 +0100721 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100722 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100723}
724
Angel Pons88521882020-01-05 20:21:20 +0100725static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100726{
727 u16 mr0reg, mch_cas, mch_wr;
728 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 +0100729 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100730
731 /* DLL Reset - self clearing - set after CLK frequency has been changed */
732 mr0reg = 0x100;
733
Angel Pons7c49cb82020-03-16 23:17:32 +0100734 /* Convert CAS to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100735 if (ctrl->CAS < 12) {
736 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
737 } else {
738 mch_cas = (u16) (ctrl->CAS - 12);
739 mch_cas = ((mch_cas << 1) | 0x1);
740 }
741
Angel Pons7c49cb82020-03-16 23:17:32 +0100742 /* Convert tWR to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100743 mch_wr = mch_wr_t[ctrl->tWR - 5];
744
Angel Pons7c49cb82020-03-16 23:17:32 +0100745 mr0reg = (mr0reg & ~0x0004) | ((mch_cas & 0x1) << 2);
746 mr0reg = (mr0reg & ~0x0070) | ((mch_cas & 0xe) << 3);
747 mr0reg = (mr0reg & ~0x0e00) | (mch_wr << 9);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100748
Angel Pons7c49cb82020-03-16 23:17:32 +0100749 /* Precharge PD - Fast (desktop) 1 or slow (mobile) 0 - mostly power-saving feature */
750 mr0reg = (mr0reg & ~(1 << 12)) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100751 return mr0reg;
752}
753
754static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
755{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200756 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100757}
758
759static u32 encode_odt(u32 odt)
760{
761 switch (odt) {
762 case 30:
763 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
764 case 60:
765 return (1 << 2); // RZQ/4
766 case 120:
767 return (1 << 6); // RZQ/2
768 default:
769 case 0:
770 return 0;
771 }
772}
773
774static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
775{
776 odtmap odt;
777 u32 mr1reg;
778
779 odt = get_ODT(ctrl, rank, channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100780 mr1reg = 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100781
782 mr1reg |= encode_odt(odt.rttnom);
783
784 return mr1reg;
785}
786
787static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
788{
789 u16 mr1reg;
790
791 mr1reg = make_mr1(ctrl, rank, channel);
792
793 write_mrreg(ctrl, channel, rank, 1, mr1reg);
794}
795
796static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
797{
798 u16 pasr, cwl, mr2reg;
799 odtmap odt;
800 int srt;
801
802 pasr = 0;
803 cwl = ctrl->CWL - 5;
804 odt = get_ODT(ctrl, rank, channel);
805
806 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
807
808 mr2reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100809 mr2reg = (mr2reg & ~0x07) | pasr;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100810 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
811 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
812 mr2reg = (mr2reg & ~0x80) | (srt << 7);
813 mr2reg |= (odt.rttwr / 60) << 9;
814
815 write_mrreg(ctrl, channel, rank, 2, mr2reg);
816}
817
818static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
819{
820 write_mrreg(ctrl, channel, rank, 3, 0);
821}
822
Angel Pons88521882020-01-05 20:21:20 +0100823void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100824{
825 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100826 int channel;
827
828 FOR_ALL_POPULATED_CHANNELS {
829 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100830 /* MR2 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100831 dram_mr2(ctrl, slotrank, channel);
832
Angel Pons7c49cb82020-03-16 23:17:32 +0100833 /* MR3 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100834 dram_mr3(ctrl, slotrank, channel);
835
Angel Pons7c49cb82020-03-16 23:17:32 +0100836 /* MR1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100837 dram_mr1(ctrl, slotrank, channel);
838
Angel Pons7c49cb82020-03-16 23:17:32 +0100839 /* MR0 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100840 dram_mr0(ctrl, slotrank, channel);
841 }
842 }
843
844 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +0100845 MCHBAR32(IOSAV_n_SP_CMD_CTRL(0)) = 0x7;
846 MCHBAR32(IOSAV_n_SUBSEQ_CTRL(0)) = 0xf1001;
Angel Pons88521882020-01-05 20:21:20 +0100847 MCHBAR32(IOSAV_n_SP_CMD_ADDR(0)) = 0x60002;
Angel Pons7c49cb82020-03-16 23:17:32 +0100848 MCHBAR32(IOSAV_n_ADDR_UPDATE(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100849
850 /* DRAM command ZQCL */
Angel Pons7c49cb82020-03-16 23:17:32 +0100851 MCHBAR32(IOSAV_n_SP_CMD_CTRL(1)) = 0x1f003;
852 MCHBAR32(IOSAV_n_SUBSEQ_CTRL(1)) = 0x1901001;
Angel Pons88521882020-01-05 20:21:20 +0100853 MCHBAR32(IOSAV_n_SP_CMD_ADDR(1)) = 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +0100854 MCHBAR32(IOSAV_n_ADDR_UPDATE(1)) = 0x288;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100855
Angel Pons7c49cb82020-03-16 23:17:32 +0100856 /* Execute command queue on all channels. Do it four times. */
857 MCHBAR32(IOSAV_SEQ_CTL) = (1 << 18) | 4;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100858
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100859 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100860 /* Wait for ref drained */
Angel Pons88521882020-01-05 20:21:20 +0100861 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100862 }
863
Angel Pons7c49cb82020-03-16 23:17:32 +0100864 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +0100865 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100866
867 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +0100868 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100869
Angel Pons88521882020-01-05 20:21:20 +0100870 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100871
872 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
873
Angel Pons7c49cb82020-03-16 23:17:32 +0100874 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100875 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100876
877 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100878 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
879 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
880 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
881 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200882
Angel Pons7c49cb82020-03-16 23:17:32 +0100883 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100884 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100885
Angel Pons7c49cb82020-03-16 23:17:32 +0100886 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100887 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100888 }
889}
890
Felix Held3b906032020-01-14 17:05:43 +0100891static const u32 lane_base[] = {
892 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
893 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
894 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100895};
896
Angel Pons88521882020-01-05 20:21:20 +0100897void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100898{
Angel Pons88521882020-01-05 20:21:20 +0100899 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100900 int lane;
901 int slotrank, slot;
902 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +0100903 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100904
905 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +0100906 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
907 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100908 }
909
910 for (slot = 0; slot < NUM_SLOTS; slot++)
911 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
912 case 0:
913 default:
Angel Pons88521882020-01-05 20:21:20 +0100914 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100915 break;
916 case 1:
Angel Pons88521882020-01-05 20:21:20 +0100917 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100918 ctrl->timings[channel][2 * slot + 0].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100919 break;
920 case 2:
Angel Pons88521882020-01-05 20:21:20 +0100921 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100922 ctrl->timings[channel][2 * slot + 1].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100923 break;
924 case 3:
Angel Pons88521882020-01-05 20:21:20 +0100925 pi_coding_ctrl[slot] =
926 (ctrl->timings[channel][2 * slot].pi_coding +
Angel Pons7c49cb82020-03-16 23:17:32 +0100927 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100928 break;
929 }
930
Angel Pons7c49cb82020-03-16 23:17:32 +0100931 /* Enable CMD XOVER */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100932 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons7c49cb82020-03-16 23:17:32 +0100933 reg32 |= (pi_coding_ctrl[0] & 0x3f) << 6;
934 reg32 |= (pi_coding_ctrl[0] & 0x40) << 9;
Angel Pons88521882020-01-05 20:21:20 +0100935 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100936 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
937
Angel Pons88521882020-01-05 20:21:20 +0100938 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100939
Angel Pons7c49cb82020-03-16 23:17:32 +0100940 /* Enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +0100941 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
942 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100943
944 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100945 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Angel Pons88521882020-01-05 20:21:20 +0100946 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100947 if (shift < 0)
948 shift = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100949
Angel Pons88521882020-01-05 20:21:20 +0100950 offset_pi_code = ctrl->pi_code_offset + shift;
Angel Pons7c49cb82020-03-16 23:17:32 +0100951
952 /* Set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +0100953 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
954 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100955 }
956
Angel Pons88521882020-01-05 20:21:20 +0100957 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
958 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100959
Angel Pons88521882020-01-05 20:21:20 +0100960 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +0100961 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100962
Angel Pons88521882020-01-05 20:21:20 +0100963 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100964
965 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100966 int post_timA_min_high = 7, pre_timA_min_high = 7;
967 int post_timA_max_high = 0, pre_timA_max_high = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100968 int shift_402x = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100969 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100970
971 if (shift < 0)
972 shift = 0;
973
974 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +0200975 post_timA_min_high = MIN(post_timA_min_high,
976 (ctrl->timings[channel][slotrank].lanes[lane].
977 timA + shift) >> 6);
978 pre_timA_min_high = MIN(pre_timA_min_high,
979 ctrl->timings[channel][slotrank].lanes[lane].
980 timA >> 6);
981 post_timA_max_high = MAX(post_timA_max_high,
982 (ctrl->timings[channel][slotrank].lanes[lane].
983 timA + shift) >> 6);
984 pre_timA_max_high = MAX(pre_timA_max_high,
985 ctrl->timings[channel][slotrank].lanes[lane].
986 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100987 }
988
989 if (pre_timA_max_high - pre_timA_min_high <
990 post_timA_max_high - post_timA_min_high)
991 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +0100992
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100993 else if (pre_timA_max_high - pre_timA_min_high >
994 post_timA_max_high - post_timA_min_high)
995 shift_402x = -1;
996
Felix Helddee167e2019-12-30 17:30:16 +0100997 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +0100998 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100999 post_timA_min_high) << (4 * slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001000
Angel Pons88521882020-01-05 20:21:20 +01001001 reg_roundtrip_latency |=
1002 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001003 shift_402x) << (8 * slotrank);
1004
1005 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001006 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001007 (((ctrl->timings[channel][slotrank].lanes[lane].
1008 timA + shift) & 0x3f)
1009 |
1010 ((ctrl->timings[channel][slotrank].lanes[lane].
1011 rising + shift) << 8)
1012 |
1013 (((ctrl->timings[channel][slotrank].lanes[lane].
1014 timA + shift -
1015 (post_timA_min_high << 6)) & 0x1c0) << 10)
1016 | ((ctrl->timings[channel][slotrank].lanes[lane].
1017 falling + shift) << 20));
1018
Felix Heldfb19c8a2020-01-14 21:27:59 +01001019 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001020 (((ctrl->timings[channel][slotrank].lanes[lane].
1021 timC + shift) & 0x3f)
1022 |
1023 (((ctrl->timings[channel][slotrank].lanes[lane].
1024 timB + shift) & 0x3f) << 8)
1025 |
1026 (((ctrl->timings[channel][slotrank].lanes[lane].
1027 timB + shift) & 0x1c0) << 9)
1028 |
1029 (((ctrl->timings[channel][slotrank].lanes[lane].
1030 timC + shift) & 0x40) << 13));
1031 }
1032 }
Angel Pons88521882020-01-05 20:21:20 +01001033 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1034 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001035}
1036
Angel Pons88521882020-01-05 20:21:20 +01001037static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001038{
Angel Pons88521882020-01-05 20:21:20 +01001039 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001040
1041 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001042 write MR3 MPR enable
1043 in this mode only RD and RDA are allowed
1044 all reads return a predefined pattern */
1045 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1046 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = (0xc01 | (ctrl->tMOD << 16));
Angel Pons88521882020-01-05 20:21:20 +01001047 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001048 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001049
1050 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001051 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1052 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4040c01;
Angel Pons88521882020-01-05 20:21:20 +01001053 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001054 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001055
1056 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001057 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1058 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x100f | ((ctrl->CAS + 36) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001059 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001060 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001061
1062 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001063 write MR3 MPR disable */
1064 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1065 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001066 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001067 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001068
Angel Pons7c49cb82020-03-16 23:17:32 +01001069 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001070 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001071
Angel Pons88521882020-01-05 20:21:20 +01001072 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001073}
1074
Angel Pons7c49cb82020-03-16 23:17:32 +01001075static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank, int lane)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001076{
1077 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Angel Pons7c49cb82020-03-16 23:17:32 +01001078
1079 return (MCHBAR32(lane_base[lane] +
1080 GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >> (timA % 32)) & 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001081}
1082
1083struct run {
1084 int middle;
1085 int end;
1086 int start;
1087 int all;
1088 int length;
1089};
1090
1091static struct run get_longest_zero_run(int *seq, int sz)
1092{
1093 int i, ls;
1094 int bl = 0, bs = 0;
1095 struct run ret;
1096
1097 ls = 0;
1098 for (i = 0; i < 2 * sz; i++)
1099 if (seq[i % sz]) {
1100 if (i - ls > bl) {
1101 bl = i - ls;
1102 bs = ls;
1103 }
1104 ls = i + 1;
1105 }
1106 if (bl == 0) {
1107 ret.middle = sz / 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001108 ret.start = 0;
1109 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001110 ret.length = sz;
Angel Pons7c49cb82020-03-16 23:17:32 +01001111 ret.all = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001112 return ret;
1113 }
1114
Angel Pons7c49cb82020-03-16 23:17:32 +01001115 ret.start = bs % sz;
1116 ret.end = (bs + bl - 1) % sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001117 ret.middle = (bs + (bl - 1) / 2) % sz;
1118 ret.length = bl;
Angel Pons7c49cb82020-03-16 23:17:32 +01001119 ret.all = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001120
1121 return ret;
1122}
1123
Angel Pons7c49cb82020-03-16 23:17:32 +01001124static void discover_timA_coarse(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001125{
1126 int timA;
1127 int statistics[NUM_LANES][128];
1128 int lane;
1129
1130 for (timA = 0; timA < 128; timA++) {
1131 FOR_ALL_LANES {
1132 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1133 }
1134 program_timings(ctrl, channel);
1135
1136 test_timA(ctrl, channel, slotrank);
1137
1138 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001139 statistics[lane][timA] = !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001140 }
1141 }
1142 FOR_ALL_LANES {
1143 struct run rn = get_longest_zero_run(statistics[lane], 128);
1144 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1145 upperA[lane] = rn.end;
1146 if (upperA[lane] < rn.middle)
1147 upperA[lane] += 128;
Angel Pons7c49cb82020-03-16 23:17:32 +01001148
Patrick Rudolph368b6152016-11-25 16:36:52 +01001149 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001150 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001151 }
1152}
1153
Angel Pons7c49cb82020-03-16 23:17:32 +01001154static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001155{
1156 int timA_delta;
1157 int statistics[NUM_LANES][51];
1158 int lane, i;
1159
1160 memset(statistics, 0, sizeof(statistics));
1161
1162 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01001163
1164 FOR_ALL_LANES {
1165 ctrl->timings[channel][slotrank].lanes[lane].timA
1166 = upperA[lane] + timA_delta + 0x40;
1167 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001168 program_timings(ctrl, channel);
1169
1170 for (i = 0; i < 100; i++) {
1171 test_timA(ctrl, channel, slotrank);
1172 FOR_ALL_LANES {
1173 statistics[lane][timA_delta + 25] +=
Angel Pons7c49cb82020-03-16 23:17:32 +01001174 does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001175 }
1176 }
1177 }
1178 FOR_ALL_LANES {
1179 int last_zero, first_all;
1180
1181 for (last_zero = -25; last_zero <= 25; last_zero++)
1182 if (statistics[lane][last_zero + 25])
1183 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01001184
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001185 last_zero--;
1186 for (first_all = -25; first_all <= 25; first_all++)
1187 if (statistics[lane][first_all + 25] == 100)
1188 break;
1189
Angel Pons7c49cb82020-03-16 23:17:32 +01001190 printram("lane %d: %d, %d\n", lane, last_zero, first_all);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001191
1192 ctrl->timings[channel][slotrank].lanes[lane].timA =
Angel Pons7c49cb82020-03-16 23:17:32 +01001193 (last_zero + first_all) / 2 + upperA[lane];
1194
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001195 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01001196 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001197 }
1198}
1199
Angel Pons891f2bc2020-01-10 01:27:28 +01001200static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001201{
1202 int works[NUM_LANES];
1203 int lane;
Angel Pons7c49cb82020-03-16 23:17:32 +01001204
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001205 while (1) {
1206 int all_works = 1, some_works = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001207
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001208 program_timings(ctrl, channel);
1209 test_timA(ctrl, channel, slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001210
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001211 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001212 works[lane] = !does_lane_work(ctrl, channel, slotrank, lane);
1213
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001214 if (works[lane])
1215 some_works = 1;
1216 else
1217 all_works = 0;
1218 }
1219 if (all_works)
1220 return 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001221
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001222 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001223 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001224 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1225 channel, slotrank);
1226 return MAKE_ERR;
1227 }
Angel Pons88521882020-01-05 20:21:20 +01001228 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001229 printram("4024 -= 2;\n");
1230 continue;
1231 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001232 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001233 printram("4028 += 2;\n");
Angel Pons7c49cb82020-03-16 23:17:32 +01001234
Felix Heldef4fe3e2019-12-31 14:15:05 +01001235 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001236 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1237 channel, slotrank);
1238 return MAKE_ERR;
1239 }
1240 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001241 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001242 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001243 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001244 }
1245 }
1246 return 0;
1247}
1248
1249struct timA_minmax {
1250 int timA_min_high, timA_max_high;
1251};
1252
Angel Pons88521882020-01-05 20:21:20 +01001253static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001254 struct timA_minmax *mnmx)
1255{
1256 int lane;
1257 mnmx->timA_min_high = 7;
1258 mnmx->timA_max_high = 0;
1259
1260 FOR_ALL_LANES {
1261 if (mnmx->timA_min_high >
1262 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1263 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001264 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001265 if (mnmx->timA_max_high <
1266 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1267 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001268 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001269 }
1270}
1271
Angel Pons88521882020-01-05 20:21:20 +01001272static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001273 struct timA_minmax *mnmx)
1274{
1275 struct timA_minmax post;
1276 int shift_402x = 0;
1277
Angel Pons7c49cb82020-03-16 23:17:32 +01001278 /* Get changed maxima */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001279 pre_timA_change(ctrl, channel, slotrank, &post);
1280
1281 if (mnmx->timA_max_high - mnmx->timA_min_high <
1282 post.timA_max_high - post.timA_min_high)
1283 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001284
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001285 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1286 post.timA_max_high - post.timA_min_high)
1287 shift_402x = -1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001288
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001289 else
1290 shift_402x = 0;
1291
Felix Heldef4fe3e2019-12-31 14:15:05 +01001292 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001293 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001294 printram("4024 += %d;\n", shift_402x);
1295 printram("4028 += %d;\n", shift_402x);
1296}
1297
Angel Pons7c49cb82020-03-16 23:17:32 +01001298/*
1299 * Compensate the skew between DQS and DQs.
1300 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001301 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1302 * The controller has to measure and compensate this skew for every byte-lane. By delaying
Angel Pons7c49cb82020-03-16 23:17:32 +01001303 * either all DQ signals or DQS signal, a full phase shift can be introduced. It is assumed
Angel Pons891f2bc2020-01-10 01:27:28 +01001304 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001305 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001306 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1307 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1308 * over all possible values to do a full phase shift and issues read commands. With DQS and
Angel Pons7c49cb82020-03-16 23:17:32 +01001309 * DQ in phase the data being read is expected to alternate on every byte:
1310 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001311 * 0xFF 0x00 0xFF ...
Angel Pons7c49cb82020-03-16 23:17:32 +01001312 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001313 * Once the controller has detected this pattern a bit in the result register is set for the
1314 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001315 */
Angel Pons88521882020-01-05 20:21:20 +01001316int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001317{
1318 int channel, slotrank, lane;
1319 int err;
1320
1321 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1322 int all_high, some_high;
1323 int upperA[NUM_LANES];
1324 struct timA_minmax mnmx;
1325
Angel Pons88521882020-01-05 20:21:20 +01001326 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001327
Felix Held2bb3cdf2018-07-28 00:23:59 +02001328 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001329 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1330 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001331 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001332 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001333
Angel Pons7c49cb82020-03-16 23:17:32 +01001334 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001335 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001336
Angel Pons88521882020-01-05 20:21:20 +01001337 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001338
Felix Heldef4fe3e2019-12-31 14:15:05 +01001339 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001340 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001341 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001342
Felix Held2bb3cdf2018-07-28 00:23:59 +02001343 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001344
Felix Held2bb3cdf2018-07-28 00:23:59 +02001345 all_high = 1;
1346 some_high = 0;
1347 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001348 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001349 some_high = 1;
1350 else
1351 all_high = 0;
1352 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001353
1354 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001355 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001356 printram("4028--;\n");
1357 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001358 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001359 upperA[lane] -= 0x40;
1360
1361 }
1362 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001363 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001364 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001365 printram("4024++;\n");
1366 printram("4028++;\n");
1367 }
1368
1369 program_timings(ctrl, channel);
1370
1371 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1372
1373 err = discover_402x(ctrl, channel, slotrank, upperA);
1374 if (err)
1375 return err;
1376
1377 post_timA_change(ctrl, channel, slotrank, &mnmx);
1378 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1379
1380 discover_timA_fine(ctrl, channel, slotrank, upperA);
1381
1382 post_timA_change(ctrl, channel, slotrank, &mnmx);
1383 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1384
1385 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001386 ctrl->timings[channel][slotrank].lanes[lane].timA -=
1387 mnmx.timA_min_high * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001388 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001389 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001390 printram("4028 -= %d;\n", mnmx.timA_min_high);
1391
1392 post_timA_change(ctrl, channel, slotrank, &mnmx);
1393
1394 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001395 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001396 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001397
1398 printram("final results:\n");
1399 FOR_ALL_LANES
Angel Pons7c49cb82020-03-16 23:17:32 +01001400 printram("Aval: %d, %d, %d: %x\n", channel, slotrank, lane,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001401 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001402
Angel Pons88521882020-01-05 20:21:20 +01001403 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001404
1405 toggle_io_reset();
1406 }
1407
1408 FOR_ALL_POPULATED_CHANNELS {
1409 program_timings(ctrl, channel);
1410 }
1411 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001412 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001413 }
1414 return 0;
1415}
1416
Angel Pons88521882020-01-05 20:21:20 +01001417static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001418{
1419 int lane;
1420
1421 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001422 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1423 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001424 }
1425
Angel Pons88521882020-01-05 20:21:20 +01001426 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001427
1428 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001429 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
1430 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
1431 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | 4 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001432 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | (6 << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +01001433 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001434
1435 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001436 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f207;
1437 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8041001;
Angel Pons88521882020-01-05 20:21:20 +01001438 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001439 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001440
1441 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01001442 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f201;
1443 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x80411f4;
Angel Pons88521882020-01-05 20:21:20 +01001444 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001445 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001446
1447 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001448 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f207;
1449 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
1450 0x08000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001451 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001452 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001453
Angel Pons7c49cb82020-03-16 23:17:32 +01001454 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001455 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001456
Angel Pons88521882020-01-05 20:21:20 +01001457 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001458
1459 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001460 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1461 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001462 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001463 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001464
1465 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001466 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f006;
1467 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
1468 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) | 8 | (ctrl->CAS << 16);
Angel Pons88521882020-01-05 20:21:20 +01001469 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001470 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001471
1472 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001473 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1474 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001475 0x40011f4 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001476 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001477 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001478
1479 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001480 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
1481 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001482 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001483 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001484
Angel Pons7c49cb82020-03-16 23:17:32 +01001485 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001486 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001487
Angel Pons88521882020-01-05 20:21:20 +01001488 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001489}
1490
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001491static void timC_threshold_process(int *data, const int count)
1492{
1493 int min = data[0];
1494 int max = min;
1495 int i;
1496 for (i = 1; i < count; i++) {
1497 if (min > data[i])
1498 min = data[i];
Angel Pons7c49cb82020-03-16 23:17:32 +01001499
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001500 if (max < data[i])
1501 max = data[i];
1502 }
Angel Pons7c49cb82020-03-16 23:17:32 +01001503 int threshold = min / 2 + max / 2;
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001504 for (i = 0; i < count; i++)
1505 data[i] = data[i] > threshold;
Angel Pons7c49cb82020-03-16 23:17:32 +01001506
Angel Pons891f2bc2020-01-10 01:27:28 +01001507 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001508}
1509
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001510static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1511{
1512 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01001513 int stats[NUM_LANES][MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001514 int lane;
1515
Angel Pons88521882020-01-05 20:21:20 +01001516 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001517
1518 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001519 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1520 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001521 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001522 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001523
Angel Pons7c49cb82020-03-16 23:17:32 +01001524 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001525 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001526
1527 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001528 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001529 program_timings(ctrl, channel);
1530
1531 test_timC(ctrl, channel, slotrank);
1532
1533 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001534 stats[lane][timC] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001535 }
1536 }
1537 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001538 struct run rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1539
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001540 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001541 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1542 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001543 /*
1544 * With command training not being done yet, the lane can be erroneous.
1545 * Take the average as reference and try again to find a run.
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001546 */
Angel Pons7c49cb82020-03-16 23:17:32 +01001547 timC_threshold_process(stats[lane], ARRAY_SIZE(stats[lane]));
1548 rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1549
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001550 if (rn.all || rn.length < 8) {
1551 printk(BIOS_EMERG, "timC recovery failed\n");
1552 return MAKE_ERR;
1553 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001554 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001555 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001556 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001557 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001558 }
1559 return 0;
1560}
1561
Angel Pons88521882020-01-05 20:21:20 +01001562static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001563{
1564 int channel, ret = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001565
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001566 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1567 ret++;
Angel Pons7c49cb82020-03-16 23:17:32 +01001568
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001569 return ret;
1570}
1571
Angel Pons88521882020-01-05 20:21:20 +01001572static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001573{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301574 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001575 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Angel Pons7c49cb82020-03-16 23:17:32 +01001576
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001577 for (j = 0; j < 16; j++)
1578 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
Angel Pons7c49cb82020-03-16 23:17:32 +01001579
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001580 sfence();
1581}
1582
Angel Pons88521882020-01-05 20:21:20 +01001583static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001584{
1585 int ret = 0;
1586 int channel;
1587 FOR_ALL_POPULATED_CHANNELS ret++;
1588 return ret;
1589}
1590
Angel Pons88521882020-01-05 20:21:20 +01001591static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001592{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301593 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001594 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301595 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01001596
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001597 for (j = 0; j < 16; j++)
1598 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
Angel Pons7c49cb82020-03-16 23:17:32 +01001599
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001600 for (j = 0; j < 16; j++)
1601 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
Angel Pons7c49cb82020-03-16 23:17:32 +01001602
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001603 sfence();
1604}
1605
Angel Pons88521882020-01-05 20:21:20 +01001606static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001607{
1608 int channel, slotrank, lane;
1609
1610 FOR_ALL_POPULATED_CHANNELS {
1611 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001612 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1613 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001614 }
1615
1616 program_timings(ctrl, channel);
1617
1618 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001619 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001620
1621 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001622 write MR3 MPR enable
1623 in this mode only RD and RDA are allowed
1624 all reads return a predefined pattern */
1625 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1626 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001627 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001628 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001629 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001630 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001631
1632 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001633 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1634 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001635 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001636 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001637
1638 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001639 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1640 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001641 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001642 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001643 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001644 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001645
1646 /* DRAM command MRS
1647 * write MR3 MPR disable */
Angel Pons7c49cb82020-03-16 23:17:32 +01001648 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1649 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001650 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001651 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001652 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001653 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001654
Angel Pons7c49cb82020-03-16 23:17:32 +01001655 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001656 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001657
Angel Pons88521882020-01-05 20:21:20 +01001658 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001659 }
1660
1661 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001662 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1663 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001664 }
1665
1666 program_timings(ctrl, channel);
1667
1668 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001669 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001670 /* DRAM command MRS
1671 * write MR3 MPR enable
1672 * in this mode only RD and RDA are allowed
1673 * all reads return a predefined pattern */
Angel Pons7c49cb82020-03-16 23:17:32 +01001674 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1675 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001676 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001677 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001678 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001679 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001680
1681 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001682 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1683 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001684 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001685 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001686
1687 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001688 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1689 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001690 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001691 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001692 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001693 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001694
1695 /* DRAM command MRS
1696 * write MR3 MPR disable */
Angel Pons7c49cb82020-03-16 23:17:32 +01001697 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1698 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001699 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001700 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001701 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001702 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001703
Angel Pons7c49cb82020-03-16 23:17:32 +01001704 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001705 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001706
Angel Pons88521882020-01-05 20:21:20 +01001707 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001708 }
1709 }
1710}
1711
Angel Pons88521882020-01-05 20:21:20 +01001712static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001713{
1714 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001715 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001716
Angel Pons88521882020-01-05 20:21:20 +01001717 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001718 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001719 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f207;
1720 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001721 0x8000c01 | ((ctrl->CWL + ctrl->tWLO) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001722 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 8 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001723 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001724
1725 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001726 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f107;
1727 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4000c01 | ((ctrl->CAS + 38) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001728 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01001729 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001730
Angel Pons7c49cb82020-03-16 23:17:32 +01001731 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001732 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001733
Angel Pons88521882020-01-05 20:21:20 +01001734 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001735
1736 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001737 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001738}
1739
1740static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1741{
1742 int timB;
1743 int statistics[NUM_LANES][128];
1744 int lane;
1745
Angel Pons88521882020-01-05 20:21:20 +01001746 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001747
1748 for (timB = 0; timB < 128; timB++) {
1749 FOR_ALL_LANES {
1750 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1751 }
1752 program_timings(ctrl, channel);
1753
1754 test_timB(ctrl, channel, slotrank);
1755
1756 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001757 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1758 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1759 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001760 }
1761 }
1762 FOR_ALL_LANES {
1763 struct run rn = get_longest_zero_run(statistics[lane], 128);
Angel Pons7c49cb82020-03-16 23:17:32 +01001764 /*
1765 * timC is a direct function of timB's 6 LSBs. Some tests increments the value
1766 * of timB by a small value, which might cause the 6-bit value to overflow if
1767 * it's close to 0x3f. Increment the value by a small offset if it's likely
1768 * to overflow, to make sure it won't overflow while running tests and bricks
1769 * the system due to a non matching timC.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001770 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001771 * TODO: find out why some tests (edge write discovery) increment timB.
1772 */
1773 if ((rn.start & 0x3f) == 0x3e)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001774 rn.start += 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001775 else if ((rn.start & 0x3f) == 0x3f)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001776 rn.start += 1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001777
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001778 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1779 if (rn.all) {
1780 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1781 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001782
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001783 return MAKE_ERR;
1784 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001785 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1786 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001787 }
1788 return 0;
1789}
1790
1791static int get_timB_high_adjust(u64 val)
1792{
1793 int i;
1794
1795 /* good */
1796 if (val == 0xffffffffffffffffLL)
1797 return 0;
1798
1799 if (val >= 0xf000000000000000LL) {
1800 /* needs negative adjustment */
1801 for (i = 0; i < 8; i++)
1802 if (val << (8 * (7 - i) + 4))
1803 return -i;
1804 } else {
1805 /* needs positive adjustment */
1806 for (i = 0; i < 8; i++)
1807 if (val >> (8 * (7 - i) + 4))
1808 return i;
1809 }
1810 return 8;
1811}
1812
Angel Pons88521882020-01-05 20:21:20 +01001813static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001814{
1815 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001816 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001817 FOR_ALL_POPULATED_CHANNELS {
1818 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001819 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001820 }
1821 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1822
Angel Pons88521882020-01-05 20:21:20 +01001823 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001824
Angel Pons88521882020-01-05 20:21:20 +01001825 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001826
1827 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001828 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
1829 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001830 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001831 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001832
1833 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001834 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f207;
1835 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8040c01;
Angel Pons88521882020-01-05 20:21:20 +01001836 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001837 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001838
1839 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01001840 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f201;
1841 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x8041003;
Angel Pons88521882020-01-05 20:21:20 +01001842 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001843 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x3e2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001844
1845 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001846 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f207;
1847 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001848 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001849 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001850 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001851
Angel Pons7c49cb82020-03-16 23:17:32 +01001852 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001853 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001854
Angel Pons88521882020-01-05 20:21:20 +01001855 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001856
1857 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001858 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1859 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | ((ctrl->tRP) << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01001860 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001861 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001862
1863 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001864 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f006;
1865 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0xc01 | ((ctrl->tRCD) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001866 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001867 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001868
1869 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001870 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x3f105;
1871 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x4000c01 | ((ctrl->tRP +
Angel Pons88521882020-01-05 20:21:20 +01001872 ctrl->timings[channel][slotrank].roundtrip_latency +
Felix Heldef4fe3e2019-12-31 14:15:05 +01001873 ctrl->timings[channel][slotrank].io_latency) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001874 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60008;
Angel Pons7c49cb82020-03-16 23:17:32 +01001875 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001876
Angel Pons7c49cb82020-03-16 23:17:32 +01001877 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001878 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Felix Held9cf1dd22018-07-31 14:52:40 +02001879
Angel Pons88521882020-01-05 20:21:20 +01001880 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001881 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001882 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01001883 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01001884 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001885 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
1886 ctrl->timings[channel][slotrank].lanes[lane].timB +=
1887 get_timB_high_adjust(res) * 64;
1888
1889 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01001890 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
1891 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001892 }
1893 }
Angel Pons88521882020-01-05 20:21:20 +01001894 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001895}
1896
Angel Pons88521882020-01-05 20:21:20 +01001897static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001898{
1899 int slotrank;
1900
Angel Pons88521882020-01-05 20:21:20 +01001901 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001902
1903 /* choose an existing rank. */
1904 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
1905
1906 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001907 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
1908 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01001909 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001910 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001911
Angel Pons7c49cb82020-03-16 23:17:32 +01001912 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001913 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001914
Angel Pons88521882020-01-05 20:21:20 +01001915 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001916}
1917
Angel Pons7c49cb82020-03-16 23:17:32 +01001918/*
1919 * Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001920 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001921 * Since DDR3 uses a fly-by topology, the data and strobes signals reach the chips at different
1922 * times with respect to command, address and clock signals. By delaying either all DQ/DQS or
1923 * all CMD/ADDR/CLK signals, a full phase shift can be introduced. It is assumed that the
1924 * CLK/ADDR/CMD signals have the same routing delay.
1925 *
1926 * To find the required phase shift the DRAM is placed in "write leveling" mode. In this mode,
1927 * the DRAM-chip samples the CLK on every DQS edge and feeds back the sampled value on the data
1928 * lanes (DQ).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001929 */
Angel Pons88521882020-01-05 20:21:20 +01001930int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001931{
1932 int channel, slotrank, lane;
1933 int err;
1934
1935 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001936 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001937
1938 FOR_ALL_POPULATED_CHANNELS {
1939 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001940 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001941 }
1942
Angel Pons7c49cb82020-03-16 23:17:32 +01001943 /* Refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01001944 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001945 FOR_ALL_POPULATED_CHANNELS {
1946 write_op(ctrl, channel);
1947 }
1948
Angel Pons7c49cb82020-03-16 23:17:32 +01001949 /* Enable write leveling on all ranks
1950 Disable all DQ outputs
1951 Only NOP is allowed in this mode */
1952 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
1953 write_mrreg(ctrl, channel, slotrank, 1,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001954 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001955
Angel Pons88521882020-01-05 20:21:20 +01001956 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001957
1958 toggle_io_reset();
1959
Angel Pons7c49cb82020-03-16 23:17:32 +01001960 /* Set any valid value for timB, it gets corrected later */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001961 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1962 err = discover_timB(ctrl, channel, slotrank);
1963 if (err)
1964 return err;
1965 }
1966
Angel Pons7c49cb82020-03-16 23:17:32 +01001967 /* Disable write leveling on all ranks */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001968 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
Angel Pons7c49cb82020-03-16 23:17:32 +01001969 write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001970
Angel Pons88521882020-01-05 20:21:20 +01001971 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001972
1973 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001974 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001975
Angel Pons7c49cb82020-03-16 23:17:32 +01001976 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01001977 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001978
1979 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001980 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
1981 MCHBAR32(IOSAV_STATUS_ch(channel));
1982 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001983
1984 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01001985 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
1986 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
Angel Pons88521882020-01-05 20:21:20 +01001987 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001988 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001989
Angel Pons7c49cb82020-03-16 23:17:32 +01001990 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001991 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001992
Angel Pons88521882020-01-05 20:21:20 +01001993 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001994 }
1995
1996 toggle_io_reset();
1997
1998 printram("CPE\n");
1999 precharge(ctrl);
2000 printram("CPF\n");
2001
2002 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002003 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002004 }
2005
2006 FOR_ALL_POPULATED_CHANNELS {
2007 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01002008 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002009 }
2010
2011 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2012 err = discover_timC(ctrl, channel, slotrank);
2013 if (err)
2014 return err;
2015 }
2016
2017 FOR_ALL_POPULATED_CHANNELS
2018 program_timings(ctrl, channel);
2019
2020 /* measure and adjust timB timings */
2021 adjust_high_timB(ctrl);
2022
2023 FOR_ALL_POPULATED_CHANNELS
2024 program_timings(ctrl, channel);
2025
2026 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002027 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002028 }
2029 return 0;
2030}
2031
Angel Pons88521882020-01-05 20:21:20 +01002032static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002033{
2034 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2035 int timC_delta;
2036 int lanes_ok = 0;
2037 int ctr = 0;
2038 int lane;
2039
2040 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2041 FOR_ALL_LANES {
2042 ctrl->timings[channel][slotrank].lanes[lane].timC =
2043 saved_rt.lanes[lane].timC + timC_delta;
2044 }
2045 program_timings(ctrl, channel);
2046 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002047 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002048 }
2049
Angel Pons88521882020-01-05 20:21:20 +01002050 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002051
Angel Pons88521882020-01-05 20:21:20 +01002052 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002053 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002054 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
2055 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002056 ((MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002057 | 8 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002058 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002059 (slotrank << 24) | ctr | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002060 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Felix Held9fe248f2018-07-31 20:59:45 +02002061
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002062 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002063 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2064 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002065 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002066 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
2067 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
Angel Pons7c49cb82020-03-16 23:17:32 +01002068 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002069
2070 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002071 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2072 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002073 0x4001020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002074 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
2075 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
Angel Pons7c49cb82020-03-16 23:17:32 +01002076 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002077
2078 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002079 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2080 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xf1001;
Angel Pons88521882020-01-05 20:21:20 +01002081 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002082 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002083
Angel Pons7c49cb82020-03-16 23:17:32 +01002084 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002085 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002086
Angel Pons88521882020-01-05 20:21:20 +01002087 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002088 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002089 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002090
2091 if (r32 == 0)
2092 lanes_ok |= 1 << lane;
2093 }
2094 ctr++;
2095 if (lanes_ok == ((1 << NUM_LANES) - 1))
2096 break;
2097 }
2098
2099 ctrl->timings[channel][slotrank] = saved_rt;
2100
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002101 return lanes_ok != ((1 << NUM_LANES) - 1);
2102}
2103
Angel Pons88521882020-01-05 20:21:20 +01002104static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002105{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302106 unsigned int i, j;
Angel Pons7c49cb82020-03-16 23:17:32 +01002107 unsigned int offset = get_precedening_channels(ctrl, channel) * 0x40;
2108 unsigned int step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002109
2110 if (patno) {
2111 u8 base8 = 0x80 >> ((patno - 1) % 8);
2112 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2113 for (i = 0; i < 32; i++) {
2114 for (j = 0; j < 16; j++) {
2115 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01002116
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002117 if (invert[patno - 1][i] & (1 << (j / 2)))
2118 val = ~val;
Angel Pons7c49cb82020-03-16 23:17:32 +01002119
2120 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002121 }
2122 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002123 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002124 for (i = 0; i < ARRAY_SIZE(pattern); i++) {
2125 for (j = 0; j < 16; j++) {
2126 const u32 val = pattern[i][j];
2127 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
2128 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002129 }
2130 sfence();
2131 }
2132}
2133
Angel Pons88521882020-01-05 20:21:20 +01002134static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002135{
2136 int channel, slotrank;
2137
2138 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002139 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002140
Angel Pons7c49cb82020-03-16 23:17:32 +01002141 /* Choose an existing rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002142 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2143
2144 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01002145 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
2146 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002147 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002148 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002149
Angel Pons7c49cb82020-03-16 23:17:32 +01002150 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002151 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002152
Angel Pons88521882020-01-05 20:21:20 +01002153 wait_for_iosav(channel);
2154 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002155 }
2156
2157 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002158 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002159 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002160 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002161
2162 /* choose an existing rank. */
2163 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2164
2165 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01002166 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
2167 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002168 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002169 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002170
Angel Pons7c49cb82020-03-16 23:17:32 +01002171 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002172 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002173
Angel Pons88521882020-01-05 20:21:20 +01002174 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002175 }
2176
Angel Pons7c49cb82020-03-16 23:17:32 +01002177 /* JEDEC reset */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002178 dram_jedecreset(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01002179
2180 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002181 dram_mrscommands(ctrl);
2182
2183 toggle_io_reset();
2184}
2185
2186#define MIN_C320C_LEN 13
2187
2188static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2189{
2190 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2191 int slotrank;
2192 int c320c;
2193 int stat[NUM_SLOTRANKS][256];
2194 int delta = 0;
2195
2196 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2197
2198 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002199 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002200 }
2201
2202 ctrl->cmd_stretch[channel] = cmd_stretch;
2203
Angel Pons88521882020-01-05 20:21:20 +01002204 MCHBAR32(TC_RAP_ch(channel)) =
Angel Pons7c49cb82020-03-16 23:17:32 +01002205 (ctrl->tRRD << 0)
2206 | (ctrl->tRTP << 4)
2207 | (ctrl->tCKE << 8)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002208 | (ctrl->tWTR << 12)
2209 | (ctrl->tFAW << 16)
Angel Pons7c49cb82020-03-16 23:17:32 +01002210 | (ctrl->tWR << 24)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002211 | (ctrl->cmd_stretch[channel] << 30);
2212
2213 if (ctrl->cmd_stretch[channel] == 2)
2214 delta = 2;
2215 else if (ctrl->cmd_stretch[channel] == 0)
2216 delta = 4;
2217
2218 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002219 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002220 }
2221
2222 for (c320c = -127; c320c <= 127; c320c++) {
2223 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002224 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002225 }
2226 program_timings(ctrl, channel);
2227 reprogram_320c(ctrl);
2228 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002229 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002230 }
2231 }
2232 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002233 struct run rn = get_longest_zero_run(stat[slotrank], 255);
2234
Angel Pons88521882020-01-05 20:21:20 +01002235 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002236 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2237 channel, slotrank, rn.start, rn.middle, rn.end);
Angel Pons7c49cb82020-03-16 23:17:32 +01002238
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002239 if (rn.all || rn.length < MIN_C320C_LEN) {
2240 FOR_ALL_POPULATED_RANKS {
2241 ctrl->timings[channel][slotrank] =
2242 saved_timings[channel][slotrank];
2243 }
2244 return MAKE_ERR;
2245 }
2246 }
2247
2248 return 0;
2249}
2250
Angel Pons7c49cb82020-03-16 23:17:32 +01002251/*
2252 * Adjust CMD phase shift and try multiple command rates.
2253 * A command rate of 2T doubles the time needed for address and command decode.
2254 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002255int command_training(ramctr_timing *ctrl)
2256{
2257 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002258
2259 FOR_ALL_POPULATED_CHANNELS {
2260 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002261 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002262 }
2263
2264 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002265 int cmdrate, err;
2266
2267 /*
2268 * Dual DIMM per channel:
Angel Pons7c49cb82020-03-16 23:17:32 +01002269 * Issue:
2270 * While c320c discovery seems to succeed raminit will fail in write training.
2271 *
2272 * Workaround:
2273 * Skip 1T in dual DIMM mode, that's only supported by a few DIMMs.
2274 * Only try 1T mode for XMP DIMMs that request it in dual DIMM mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002275 *
2276 * Single DIMM per channel:
2277 * Try command rate 1T and 2T
2278 */
2279 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002280 if (ctrl->tCMD)
2281 /* XMP gives the CMD rate in clock ticks, not ns */
2282 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002283
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002284 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002285 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2286
2287 if (!err)
2288 break;
2289 }
2290
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002291 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002292 printk(BIOS_EMERG, "c320c discovery failed\n");
2293 return err;
2294 }
2295
Angel Pons891f2bc2020-01-10 01:27:28 +01002296 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002297 }
2298
2299 FOR_ALL_POPULATED_CHANNELS
2300 program_timings(ctrl, channel);
2301
2302 reprogram_320c(ctrl);
2303 return 0;
2304}
2305
Angel Pons891f2bc2020-01-10 01:27:28 +01002306static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002307{
2308 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002309 int stats[NUM_LANES][MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002310 int lane;
2311
2312 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2313 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002314 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
Angel Pons891f2bc2020-01-10 01:27:28 +01002315 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002316 }
2317 program_timings(ctrl, channel);
2318
2319 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002320 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2321 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002322 }
2323
Angel Pons88521882020-01-05 20:21:20 +01002324 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002325
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002326 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002327 write MR3 MPR enable
2328 in this mode only RD and RDA are allowed
2329 all reads return a predefined pattern */
2330 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2331 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002332 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002333 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002334
2335 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002336 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2337 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x40411f4;
Angel Pons88521882020-01-05 20:21:20 +01002338 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002339 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002340
2341 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002342 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2343 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002344 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002345 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002346
2347 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002348 MR3 disable MPR */
2349 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2350 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002351 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002352 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002353
Angel Pons7c49cb82020-03-16 23:17:32 +01002354 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002355 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002356
Angel Pons88521882020-01-05 20:21:20 +01002357 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002358
2359 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002360 stats[lane][edge] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002361 }
2362 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002363
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002364 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002365 struct run rn = get_longest_zero_run(stats[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002366 edges[lane] = rn.middle;
Angel Pons7c49cb82020-03-16 23:17:32 +01002367
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002368 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002369 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n", channel,
2370 slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002371 return MAKE_ERR;
2372 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002373 printram("eval %d, %d, %d: %02x\n", channel, slotrank, lane, edges[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002374 }
2375 return 0;
2376}
2377
2378int discover_edges(ramctr_timing *ctrl)
2379{
2380 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2381 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2382 int channel, slotrank, lane;
2383 int err;
2384
Angel Pons88521882020-01-05 20:21:20 +01002385 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002386
2387 toggle_io_reset();
2388
2389 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002390 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002391 }
2392
2393 FOR_ALL_POPULATED_CHANNELS {
2394 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002395 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002396 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002397 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002398 }
2399
2400 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002401 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2402 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002403 }
2404
2405 program_timings(ctrl, channel);
2406
2407 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002408 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002409
2410 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002411 MR3 enable MPR
2412 write MR3 MPR enable
2413 in this mode only RD and RDA are allowed
2414 all reads return a predefined pattern */
2415 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2416 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002417 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002418 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002419 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002420 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002421
2422 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002423 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2424 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01002425 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002426 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002427
2428 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002429 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2430 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002431 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002432 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002433 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002434 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002435
2436 /* DRAM command MRS
2437 * MR3 disable MPR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002438 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2439 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002440 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002441 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002442 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002443 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02002444
Angel Pons7c49cb82020-03-16 23:17:32 +01002445 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002446 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002447
Angel Pons88521882020-01-05 20:21:20 +01002448 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002449 }
2450
2451 /* XXX: check any measured value ? */
2452
2453 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002454 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
Angel Pons7c49cb82020-03-16 23:17:32 +01002455 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002456 }
2457
2458 program_timings(ctrl, channel);
2459
2460 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002461 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002462
2463 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002464 MR3 enable MPR
2465 write MR3 MPR enable
2466 in this mode only RD and RDA are allowed
2467 all reads return a predefined pattern */
2468 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2469 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002470 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002471 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002472 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002473 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002474
2475 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002476 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2477 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons88521882020-01-05 20:21:20 +01002478 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Angel Pons63ae8de2020-01-10 02:03:47 +01002479 (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002480 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002481
2482 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002483 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2484 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002485 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002486 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002487 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002488 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002489
2490 /* DRAM command MRS
2491 * MR3 disable MPR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002492 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2493 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002494 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002495 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002496 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002497 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002498
Angel Pons7c49cb82020-03-16 23:17:32 +01002499 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002500 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002501
Angel Pons88521882020-01-05 20:21:20 +01002502 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002503 }
2504
2505 /* XXX: check any measured value ? */
2506
2507 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002508 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002509 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002510 }
2511
2512 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002513 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002514 }
2515
2516 /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */
Angel Pons88521882020-01-05 20:21:20 +01002517 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2518 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002519
2520 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2521 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002522 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002523 if (err)
2524 return err;
2525 }
2526
Angel Pons88521882020-01-05 20:21:20 +01002527 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2528 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002529
2530 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2531 err = discover_edges_real(ctrl, channel, slotrank,
2532 rising_edges[channel][slotrank]);
2533 if (err)
2534 return err;
2535 }
2536
Angel Pons88521882020-01-05 20:21:20 +01002537 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002538
2539 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2540 ctrl->timings[channel][slotrank].lanes[lane].falling =
2541 falling_edges[channel][slotrank][lane];
2542 ctrl->timings[channel][slotrank].lanes[lane].rising =
2543 rising_edges[channel][slotrank][lane];
2544 }
2545
2546 FOR_ALL_POPULATED_CHANNELS {
2547 program_timings(ctrl, channel);
2548 }
2549
2550 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002551 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002552 }
2553 return 0;
2554}
2555
Angel Pons7c49cb82020-03-16 23:17:32 +01002556static int discover_edges_write_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002557{
2558 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002559 u32 raw_stats[MAX_EDGE_TIMING + 1];
2560 int stats[MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002561 const int reg3000b24[] = { 0, 0xc, 0x2c };
2562 int lane, i;
2563 int lower[NUM_LANES];
2564 int upper[NUM_LANES];
2565 int pat;
2566
2567 FOR_ALL_LANES {
2568 lower[lane] = 0;
2569 upper[lane] = MAX_EDGE_TIMING;
2570 }
2571
2572 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002573 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002574 printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
2575
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002576 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2577 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002578 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002579 printram("using pattern %d\n", pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002580
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002581 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2582 FOR_ALL_LANES {
2583 ctrl->timings[channel][slotrank].lanes[lane].
2584 rising = edge;
2585 ctrl->timings[channel][slotrank].lanes[lane].
2586 falling = edge;
2587 }
2588 program_timings(ctrl, channel);
2589
2590 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002591 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2592 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002593 }
Angel Pons88521882020-01-05 20:21:20 +01002594 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002595
2596 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002597 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
2598 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002599 0x4 | (ctrl->tRCD << 16) |
Angel Pons891f2bc2020-01-10 01:27:28 +01002600 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10);
Angel Pons88521882020-01-05 20:21:20 +01002601 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002602 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002603 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002604
2605 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002606 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2607 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8005020 |
Felix Held2bb3cdf2018-07-28 00:23:59 +02002608 ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002609 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002610 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002611 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002612
2613 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002614 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2615 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002616 0x4005020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002617 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002618 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002619 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002620
2621 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002622 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2623 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002624 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002625 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002626 (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002627 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002628
Angel Pons7c49cb82020-03-16 23:17:32 +01002629 /* Execute command queue */
2630 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002631
Angel Pons88521882020-01-05 20:21:20 +01002632 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002633 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002634 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002635 }
2636
Angel Pons7c49cb82020-03-16 23:17:32 +01002637 /* FIXME: This register only exists on Ivy Bridge */
2638 raw_stats[edge] = MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002639 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002640
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002641 FOR_ALL_LANES {
2642 struct run rn;
2643 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
Angel Pons7c49cb82020-03-16 23:17:32 +01002644 stats[edge] = !!(raw_stats[edge] & (1 << lane));
2645
2646 rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
2647
2648 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, "
2649 "0x%02x-0x%02x\n", channel, slotrank, i, rn.start,
2650 rn.middle, rn.end, rn.start + ctrl->edge_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002651 rn.end - ctrl->edge_offset[i]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002652
2653 lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
2654 upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
2655
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002656 edges[lane] = (lower[lane] + upper[lane]) / 2;
2657 if (rn.all || (lower[lane] > upper[lane])) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002658 printk(BIOS_EMERG, "edge write discovery failed: "
2659 "%d, %d, %d\n", channel, slotrank, lane);
2660
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002661 return MAKE_ERR;
2662 }
2663 }
2664 }
2665 }
2666
Angel Pons88521882020-01-05 20:21:20 +01002667 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002668 printram("CPA\n");
2669 return 0;
2670}
2671
2672int discover_edges_write(ramctr_timing *ctrl)
2673{
2674 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
Angel Pons7c49cb82020-03-16 23:17:32 +01002675 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2676 int channel, slotrank, lane, err;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002677
Angel Pons7c49cb82020-03-16 23:17:32 +01002678 /*
2679 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2680 * also use a single loop. It would seem that it is a debugging configuration.
2681 */
Angel Pons88521882020-01-05 20:21:20 +01002682 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2683 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002684
2685 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2686 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002687 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002688 if (err)
2689 return err;
2690 }
2691
Angel Pons88521882020-01-05 20:21:20 +01002692 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2693 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002694
2695 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2696 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002697 rising_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002698 if (err)
2699 return err;
2700 }
2701
Angel Pons88521882020-01-05 20:21:20 +01002702 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002703
2704 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2705 ctrl->timings[channel][slotrank].lanes[lane].falling =
Angel Pons7c49cb82020-03-16 23:17:32 +01002706 falling_edges[channel][slotrank][lane];
2707
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002708 ctrl->timings[channel][slotrank].lanes[lane].rising =
Angel Pons7c49cb82020-03-16 23:17:32 +01002709 rising_edges[channel][slotrank][lane];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002710 }
2711
2712 FOR_ALL_POPULATED_CHANNELS
2713 program_timings(ctrl, channel);
2714
2715 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002716 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002717 }
2718 return 0;
2719}
2720
2721static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2722{
Angel Pons88521882020-01-05 20:21:20 +01002723 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002724
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002725 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002726 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0001f006;
2727 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002728 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | (ctrl->tRCD << 16) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01002729 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2730 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x0244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002731
2732 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002733 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2734 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002735 0x80011e0 | ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002736 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002737 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002738
2739 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002740 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2741 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x40011e0 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002742 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002743 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002744
2745 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002746 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2747 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x1001 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002748 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002749 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002750
Angel Pons7c49cb82020-03-16 23:17:32 +01002751 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002752 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002753
Angel Pons88521882020-01-05 20:21:20 +01002754 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002755}
2756
2757int discover_timC_write(ramctr_timing *ctrl)
2758{
Angel Pons7c49cb82020-03-16 23:17:32 +01002759 const u8 rege3c_b24[3] = { 0, 0x0f, 0x2f };
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002760 int i, pat;
2761
2762 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2763 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2764 int channel, slotrank, lane;
2765
2766 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2767 lower[channel][slotrank][lane] = 0;
2768 upper[channel][slotrank][lane] = MAX_TIMC;
2769 }
2770
Angel Pons88521882020-01-05 20:21:20 +01002771 /*
2772 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2773 * FIXME: This must only be done on Ivy Bridge.
2774 */
2775 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002776 printram("discover timC write:\n");
2777
2778 for (i = 0; i < 3; i++)
2779 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002780
2781 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
2782 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel),
2783 ~0x3f000000, rege3c_b24[i] << 24);
2784
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002785 udelay(2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002786
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002787 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2788 FOR_ALL_POPULATED_RANKS {
2789 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01002790 u32 raw_stats[MAX_TIMC + 1];
2791 int stats[MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002792
2793 /* Make sure rn.start < rn.end */
Angel Pons7c49cb82020-03-16 23:17:32 +01002794 stats[MAX_TIMC] = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002795
2796 fill_pattern5(ctrl, channel, pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002797 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
2798
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002799 for (timC = 0; timC < MAX_TIMC; timC++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002800 FOR_ALL_LANES {
2801 ctrl->timings[channel][slotrank]
2802 .lanes[lane].timC = timC;
2803 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002804 program_timings(ctrl, channel);
2805
2806 test_timC_write (ctrl, channel, slotrank);
2807
Angel Pons7c49cb82020-03-16 23:17:32 +01002808 /* FIXME: Another IVB-only register! */
2809 raw_stats[timC] =
Angel Pons1aba2a32020-01-05 22:31:41 +01002810 MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002811 }
2812 FOR_ALL_LANES {
2813 struct run rn;
Angel Pons7c49cb82020-03-16 23:17:32 +01002814 for (timC = 0; timC < MAX_TIMC; timC++) {
2815 stats[timC] = !!(raw_stats[timC]
2816 & (1 << lane));
2817 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002818
Angel Pons7c49cb82020-03-16 23:17:32 +01002819 rn = get_longest_zero_run(stats, MAX_TIMC + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002820 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002821 printk(BIOS_EMERG,
2822 "timC write discovery failed: "
2823 "%d, %d, %d\n", channel,
2824 slotrank, lane);
2825
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002826 return MAKE_ERR;
2827 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002828 printram("timC: %d, %d, %d: "
2829 "0x%02x-0x%02x-0x%02x, "
2830 "0x%02x-0x%02x\n", channel, slotrank,
2831 i, rn.start, rn.middle, rn.end,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002832 rn.start + ctrl->timC_offset[i],
Angel Pons7c49cb82020-03-16 23:17:32 +01002833 rn.end - ctrl->timC_offset[i]);
2834
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002835 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002836 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002837 lower[channel][slotrank][lane]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002838
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002839 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002840 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002841 upper[channel][slotrank][lane]);
2842
2843 }
2844 }
2845 }
2846 }
2847
2848 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002849 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
Angel Pons88521882020-01-05 20:21:20 +01002850 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002851 udelay(2);
2852 }
2853
Angel Pons88521882020-01-05 20:21:20 +01002854 /*
2855 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2856 * FIXME: This must only be done on Ivy Bridge.
2857 */
2858 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002859
2860 printram("CPB\n");
2861
2862 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002863 printram("timC %d, %d, %d: %x\n", channel, slotrank, lane,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002864 (lower[channel][slotrank][lane] +
2865 upper[channel][slotrank][lane]) / 2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002866
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002867 ctrl->timings[channel][slotrank].lanes[lane].timC =
2868 (lower[channel][slotrank][lane] +
2869 upper[channel][slotrank][lane]) / 2;
2870 }
2871 FOR_ALL_POPULATED_CHANNELS {
2872 program_timings(ctrl, channel);
2873 }
2874 return 0;
2875}
2876
Angel Pons88521882020-01-05 20:21:20 +01002877void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002878{
2879 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002880 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002881
2882 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2883 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002884 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002885 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002886 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01002887 printram("normalize %d, %d, %d: mat %d\n",
2888 channel, slotrank, lane, mat);
2889
Felix Heldef4fe3e2019-12-31 14:15:05 +01002890 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01002891 printram("normalize %d, %d, %d: delta %d\n",
2892 channel, slotrank, lane, delta);
2893
Angel Pons88521882020-01-05 20:21:20 +01002894 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01002895 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002896 }
2897
2898 FOR_ALL_POPULATED_CHANNELS {
2899 program_timings(ctrl, channel);
2900 }
2901}
2902
Angel Pons88521882020-01-05 20:21:20 +01002903void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002904{
2905 int channel, slotrank;
2906
2907 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002908 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002909 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01002910 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002911 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002912 }
2913}
2914
2915int channel_test(ramctr_timing *ctrl)
2916{
2917 int channel, slotrank, lane;
2918
2919 slotrank = 0;
2920 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002921 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01002922 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002923 return MAKE_ERR;
2924 }
2925 FOR_ALL_POPULATED_CHANNELS {
2926 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
2927
Angel Pons88521882020-01-05 20:21:20 +01002928 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002929 }
2930
2931 for (slotrank = 0; slotrank < 4; slotrank++)
2932 FOR_ALL_CHANNELS
2933 if (ctrl->rankmap[channel] & (1 << slotrank)) {
2934 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002935 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
2936 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002937 }
Angel Pons88521882020-01-05 20:21:20 +01002938 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002939
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002940 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002941 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0001f006;
2942 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x0028a004;
Angel Pons891f2bc2020-01-10 01:27:28 +01002943 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x00060000 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002944 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x00000244;
Felix Held9cf1dd22018-07-31 14:52:40 +02002945
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002946 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002947 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x0001f201;
2948 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x08281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002949 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002950 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002951
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002952 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002953 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x0001f105;
2954 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x04281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002955 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002956 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002957
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002958 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002959 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x0001f002;
2960 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x00280c01;
Angel Pons891f2bc2020-01-10 01:27:28 +01002961 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = 0x00060400 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002962 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x00000240;
Felix Held9cf1dd22018-07-31 14:52:40 +02002963
Angel Pons7c49cb82020-03-16 23:17:32 +01002964 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002965 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002966
Angel Pons88521882020-01-05 20:21:20 +01002967 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002968 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01002969 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002970 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
2971 channel, slotrank, lane);
2972 return MAKE_ERR;
2973 }
2974 }
2975 return 0;
2976}
2977
Angel Pons88521882020-01-05 20:21:20 +01002978void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002979{
2980 int channel;
2981
Angel Pons7c49cb82020-03-16 23:17:32 +01002982 /* 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 +01002983 static u32 seeds[NUM_CHANNELS][3] = {
2984 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
2985 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
2986 };
2987 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002988 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002989 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
2990 MCHBAR32(SCRAMBLING_SEED_2_HI_ch(channel)) = seeds[channel][1];
2991 MCHBAR32(SCRAMBLING_SEED_2_LO_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002992 }
2993}
2994
Angel Pons7c49cb82020-03-16 23:17:32 +01002995void set_wmm_behavior(void)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002996{
Angel Pons7c49cb82020-03-16 23:17:32 +01002997 u32 cpu = cpu_get_cpuid();
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002998
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002999 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003000 MCHBAR32(SC_WDBWM) = 0x141d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003001 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01003002 MCHBAR32(SC_WDBWM) = 0x551d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003003 }
3004}
3005
Angel Pons88521882020-01-05 20:21:20 +01003006void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003007{
3008 int channel;
3009
3010 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003011 /* Always drive command bus */
Angel Pons88521882020-01-05 20:21:20 +01003012 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003013 }
3014
3015 udelay(1);
3016
3017 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003018 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003019 }
3020}
3021
Angel Pons7c49cb82020-03-16 23:17:32 +01003022void set_read_write_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003023{
3024 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003025
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003026 FOR_ALL_POPULATED_CHANNELS {
3027 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003028 int min_pi = 10000;
3029 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003030
3031 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003032 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3033 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003034 }
3035
Angel Pons7c49cb82020-03-16 23:17:32 +01003036 b20 = (max_pi - min_pi > 51) ? 0 : ctrl->ref_card_offset[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003037
Angel Pons7c49cb82020-03-16 23:17:32 +01003038 b4_8_12 = (ctrl->pi_coding_threshold < max_pi - min_pi) ? 0x3330 : 0x2220;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003039
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003040 dram_odt_stretch(ctrl, channel);
3041
Angel Pons7c49cb82020-03-16 23:17:32 +01003042 MCHBAR32(TC_RWP_ch(channel)) = 0x0a000000 | (b20 << 20) |
Felix Held2463aa92018-07-29 21:37:55 +02003043 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003044 }
3045}
3046
Angel Pons88521882020-01-05 20:21:20 +01003047void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003048{
3049 int channel;
3050 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003051 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3052 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003053 }
3054}
3055
Angel Pons7c49cb82020-03-16 23:17:32 +01003056/* Encode the watermark latencies in a suitable format for graphics drivers consumption */
3057static int encode_wm(int ns)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003058{
Angel Pons88521882020-01-05 20:21:20 +01003059 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003060}
3061
Angel Pons7c49cb82020-03-16 23:17:32 +01003062/* FIXME: values in this function should be hardware revision-dependent */
Angel Pons88521882020-01-05 20:21:20 +01003063void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003064{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003065 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3066
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003067 int channel;
3068 int t1_cycles = 0, t1_ns = 0, t2_ns;
3069 int t3_ns;
3070 u32 r32;
3071
Angel Pons7c49cb82020-03-16 23:17:32 +01003072 /* FIXME: This register only exists on Ivy Bridge */
3073 MCHBAR32(WMM_READ_CONFIG) = 0x46;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003074
Felix Heldf9b826a2018-07-30 17:56:52 +02003075 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003076 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xffffcfff, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003077
Patrick Rudolph74203de2017-11-20 11:57:01 +01003078 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003079 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003080 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003081 else
Angel Pons7c49cb82020-03-16 23:17:32 +01003082 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003083 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003084
Felix Heldf9b826a2018-07-30 17:56:52 +02003085 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003086 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003087
Angel Pons88521882020-01-05 20:21:20 +01003088 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3089 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003090
3091 FOR_ALL_CHANNELS {
3092 switch (ctrl->rankmap[channel]) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003093 /* Unpopulated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003094 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003095 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003096 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003097 /* Only single-ranked dimms */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003098 case 1:
3099 case 4:
3100 case 5:
Angel Pons7c49cb82020-03-16 23:17:32 +01003101 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x00373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003102 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003103 /* Dual-ranked dimms present */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003104 default:
Angel Pons7c49cb82020-03-16 23:17:32 +01003105 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x009b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003106 break;
3107 }
3108 }
3109
Felix Held50b7ed22019-12-30 20:41:54 +01003110 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
Angel Pons7c49cb82020-03-16 23:17:32 +01003111 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0x00ffffff, 0x00e4d5d0);
Felix Held50b7ed22019-12-30 20:41:54 +01003112 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003113
3114 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003115 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~(3 << 16), 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003116
Angel Pons88521882020-01-05 20:21:20 +01003117 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3118 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3119 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003120
Angel Pons7c49cb82020-03-16 23:17:32 +01003121 /* Find a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003122 FOR_ALL_POPULATED_CHANNELS
3123 break;
3124
Angel Pons88521882020-01-05 20:21:20 +01003125 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3126 r32 = MCHBAR32(PM_DLL_CONFIG);
Angel Pons7c49cb82020-03-16 23:17:32 +01003127 if (r32 & (1 << 17))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003128 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003129 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003130 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
Angel Pons7c49cb82020-03-16 23:17:32 +01003131 if (!(r32 & (1 << 17)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003132 t1_ns += 500;
3133
Angel Pons88521882020-01-05 20:21:20 +01003134 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003135 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003136 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003137 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003138 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003139 t3_ns = 500;
3140 }
Angel Pons7c49cb82020-03-16 23:17:32 +01003141
3142 /* The graphics driver will use these watermark values */
3143 printk(BIOS_DEBUG, "t123: %d, %d, %d\n", t1_ns, t2_ns, t3_ns);
3144 MCHBAR32_AND_OR(SSKPD, 0xC0C0C0C0,
3145 ((encode_wm(t1_ns) + encode_wm(t2_ns)) << 16) | (encode_wm(t1_ns) << 8) |
3146 ((encode_wm(t3_ns) + encode_wm(t2_ns) + encode_wm(t1_ns)) << 24) | 0x0c);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003147}
3148
Angel Pons88521882020-01-05 20:21:20 +01003149void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003150{
3151 int channel, slotrank, lane;
3152
Angel Pons7c49cb82020-03-16 23:17:32 +01003153 FOR_ALL_POPULATED_CHANNELS {
3154 MCHBAR32(TC_RAP_ch(channel)) =
3155 (ctrl->tRRD << 0)
3156 | (ctrl->tRTP << 4)
3157 | (ctrl->tCKE << 8)
3158 | (ctrl->tWTR << 12)
3159 | (ctrl->tFAW << 16)
3160 | (ctrl->tWR << 24)
3161 | (ctrl->cmd_stretch[channel] << 30);
3162 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003163
3164 udelay(1);
3165
3166 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003167 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003168 }
3169
3170 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003171 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003172 }
3173
3174 FOR_ALL_POPULATED_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003175 MCHBAR32_OR(TC_RWP_ch(channel), 0x08000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003176
3177 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003178 udelay(1);
3179 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x00200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003180 }
3181
3182 printram("CPE\n");
3183
Angel Pons88521882020-01-05 20:21:20 +01003184 MCHBAR32(GDCRTRAININGMOD) = 0;
3185 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003186
3187 printram("CP5b\n");
3188
3189 FOR_ALL_POPULATED_CHANNELS {
3190 program_timings(ctrl, channel);
3191 }
3192
3193 u32 reg, addr;
3194
Angel Pons7c49cb82020-03-16 23:17:32 +01003195 /* Poll for RCOMP */
3196 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
3197 ;
3198
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003199 do {
Angel Pons88521882020-01-05 20:21:20 +01003200 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003201 } while ((reg & 0x14) == 0);
3202
Angel Pons7c49cb82020-03-16 23:17:32 +01003203 /* Set state of memory controller */
Angel Pons88521882020-01-05 20:21:20 +01003204 MCHBAR32(MC_INIT_STATE_G) = 0x116;
Angel Pons7c49cb82020-03-16 23:17:32 +01003205 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003206
Angel Pons7c49cb82020-03-16 23:17:32 +01003207 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003208 udelay(500);
3209
3210 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003211 /* Set valid rank CKE */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003212 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01003213 reg = (reg & ~0x0f) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003214 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003215 MCHBAR32(addr) = reg;
3216
Angel Pons7c49cb82020-03-16 23:17:32 +01003217 /* Wait 10ns for ranks to settle */
3218 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003219
3220 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3221 MCHBAR32(addr) = reg;
3222
Angel Pons7c49cb82020-03-16 23:17:32 +01003223 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003224 write_reset(ctrl);
3225 }
3226
Angel Pons7c49cb82020-03-16 23:17:32 +01003227 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003228 dram_mrscommands(ctrl);
3229
3230 printram("CP5c\n");
3231
Angel Pons88521882020-01-05 20:21:20 +01003232 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003233
3234 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003235 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003236 udelay(2);
3237 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003238}