blob: 41d683b18aad9fa5f4cec1c4026347139a931bc0 [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{
Angel Pons89ae6b82020-03-21 13:23:32 +0100171 u32 addr, 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 */
Angel Pons89ae6b82020-03-21 13:23:32 +0100178 if (IS_SANDY_CPU(ctrl->cpu) && IS_SANDY_CPU_C(ctrl->cpu)) {
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100179 if (stretch == 2)
180 stretch = 3;
Angel Pons7c49cb82020-03-16 23:17:32 +0100181
Angel Pons88521882020-01-05 20:21:20 +0100182 addr = SCHED_SECOND_CBIT_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100183 MCHBAR32_AND_OR(addr, 0xffffc3ff, (stretch << 12) | (stretch << 10));
184 printk(RAM_DEBUG, "OTHP Workaround [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100185 } else {
Angel Pons88521882020-01-05 20:21:20 +0100186 addr = TC_OTHP_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100187 MCHBAR32_AND_OR(addr, 0xfff0ffff, (stretch << 16) | (stretch << 18));
Iru Cai89af71c2018-08-16 16:46:27 +0800188 printk(RAM_DEBUG, "OTHP [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100189 }
190}
191
192void dram_timing_regs(ramctr_timing *ctrl)
193{
194 u32 reg, addr, val32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100195 int channel;
196
197 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100198 /* BIN parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100199 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100200 reg |= (ctrl->tRCD << 0);
201 reg |= (ctrl->tRP << 4);
202 reg |= (ctrl->CAS << 8);
203 reg |= (ctrl->CWL << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100204 reg |= (ctrl->tRAS << 16);
Angel Pons88521882020-01-05 20:21:20 +0100205 printram("DBP [%x] = %x\n", TC_DBP_ch(channel), reg);
206 MCHBAR32(TC_DBP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100207
Angel Pons7c49cb82020-03-16 23:17:32 +0100208 /* Regular access parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100209 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100210 reg |= (ctrl->tRRD << 0);
211 reg |= (ctrl->tRTP << 4);
212 reg |= (ctrl->tCKE << 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100213 reg |= (ctrl->tWTR << 12);
214 reg |= (ctrl->tFAW << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +0100215 reg |= (ctrl->tWR << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100216 reg |= (3 << 30);
Angel Pons88521882020-01-05 20:21:20 +0100217 printram("RAP [%x] = %x\n", TC_RAP_ch(channel), reg);
218 MCHBAR32(TC_RAP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100219
Angel Pons7c49cb82020-03-16 23:17:32 +0100220 /* Other parameters */
Angel Pons88521882020-01-05 20:21:20 +0100221 addr = TC_OTHP_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100222 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100223 reg |= (ctrl->tXPDLL << 0);
224 reg |= (ctrl->tXP << 5);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100225 reg |= (ctrl->tAONPD << 8);
226 reg |= 0xa0000;
227 printram("OTHP [%x] = %x\n", addr, reg);
228 MCHBAR32(addr) = reg;
229
Angel Pons7c49cb82020-03-16 23:17:32 +0100230 /* FIXME: This register might as well not exist */
Angel Pons1aba2a32020-01-05 22:31:41 +0100231 MCHBAR32(0x4014 + channel * 0x400) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100232
Felix Held9fe248f2018-07-31 20:59:45 +0200233 MCHBAR32_OR(addr, 0x00020000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100234
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100235 dram_odt_stretch(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100236
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100237 /*
Angel Pons7c49cb82020-03-16 23:17:32 +0100238 * TC-Refresh timing parameters:
239 * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow
240 * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024.
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100241 */
242 val32 = MIN((ctrl->tREFI * 89) / 10, (70000 << 8) / ctrl->tCK);
243
Angel Pons7c49cb82020-03-16 23:17:32 +0100244 reg = ((ctrl->tREFI & 0xffff) << 0) |
245 ((ctrl->tRFC & 0x01ff) << 16) | (((val32 / 1024) & 0x7f) << 25);
246
Angel Pons88521882020-01-05 20:21:20 +0100247 printram("REFI [%x] = %x\n", TC_RFTP_ch(channel), reg);
248 MCHBAR32(TC_RFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100249
Angel Pons88521882020-01-05 20:21:20 +0100250 MCHBAR32_OR(TC_RFP_ch(channel), 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100251
Angel Pons7c49cb82020-03-16 23:17:32 +0100252 /* Self-refresh timing parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100253 reg = 0;
254 val32 = tDLLK;
Angel Pons7c49cb82020-03-16 23:17:32 +0100255 reg = (reg & ~0x00000fff) | (val32 << 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100256 val32 = ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100257 reg = (reg & ~0x0000f000) | (val32 << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100258 val32 = tDLLK - ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100259 reg = (reg & ~0x03ff0000) | (val32 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100260 val32 = ctrl->tMOD - 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100261 reg = (reg & ~0xf0000000) | (val32 << 28);
262 printram("SRFTP [%x] = %x\n", TC_SRFTP_ch(channel), reg);
Angel Pons88521882020-01-05 20:21:20 +0100263 MCHBAR32(TC_SRFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100264 }
265}
266
267void dram_dimm_mapping(ramctr_timing *ctrl)
268{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100269 int channel;
270 dimm_info *info = &ctrl->info;
271
272 FOR_ALL_CHANNELS {
Nico Huberac4f2162017-10-01 18:14:43 +0200273 dimm_attr *dimmA, *dimmB;
274 u32 reg = 0;
275
Angel Pons7c49cb82020-03-16 23:17:32 +0100276 if (info->dimm[channel][0].size_mb >= info->dimm[channel][1].size_mb) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100277 dimmA = &info->dimm[channel][0];
278 dimmB = &info->dimm[channel][1];
Angel Pons7c49cb82020-03-16 23:17:32 +0100279 reg |= (0 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100280 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100281 dimmA = &info->dimm[channel][1];
282 dimmB = &info->dimm[channel][0];
Angel Pons7c49cb82020-03-16 23:17:32 +0100283 reg |= (1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100284 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100285
Nico Huberac4f2162017-10-01 18:14:43 +0200286 if (dimmA && (dimmA->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100287 reg |= (dimmA->size_mb / 256) << 0;
288 reg |= (dimmA->ranks - 1) << 17;
Nico Huberac4f2162017-10-01 18:14:43 +0200289 reg |= (dimmA->width / 8 - 1) << 19;
290 }
291
292 if (dimmB && (dimmB->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100293 reg |= (dimmB->size_mb / 256) << 8;
294 reg |= (dimmB->ranks - 1) << 18;
Nico Huberac4f2162017-10-01 18:14:43 +0200295 reg |= (dimmB->width / 8 - 1) << 20;
296 }
297
Angel Pons7c49cb82020-03-16 23:17:32 +0100298 reg |= 1 << 21; /* Rank interleave */
299 reg |= 1 << 22; /* Enhanced interleave */
Nico Huberac4f2162017-10-01 18:14:43 +0200300
Angel Pons7c49cb82020-03-16 23:17:32 +0100301 if ((dimmA && (dimmA->ranks > 0)) || (dimmB && (dimmB->ranks > 0))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100302 ctrl->mad_dimm[channel] = reg;
303 } else {
304 ctrl->mad_dimm[channel] = 0;
305 }
306 }
307}
308
Angel Pons88521882020-01-05 20:21:20 +0100309void dram_dimm_set_mapping(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100310{
311 int channel;
312 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100313 MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100314 }
315}
316
Angel Pons88521882020-01-05 20:21:20 +0100317void dram_zones(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100318{
319 u32 reg, ch0size, ch1size;
320 u8 val;
321 reg = 0;
322 val = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100323
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100324 if (training) {
325 ch0size = ctrl->channel_size_mb[0] ? 256 : 0;
326 ch1size = ctrl->channel_size_mb[1] ? 256 : 0;
327 } else {
328 ch0size = ctrl->channel_size_mb[0];
329 ch1size = ctrl->channel_size_mb[1];
330 }
331
332 if (ch0size >= ch1size) {
Angel Pons88521882020-01-05 20:21:20 +0100333 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100334 val = ch1size / 256;
335 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100336 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100337 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100338 MCHBAR32(MAD_CHNL) = 0x24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100339
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100340 } else {
Angel Pons88521882020-01-05 20:21:20 +0100341 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100342 val = ch0size / 256;
343 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100344 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100345 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100346 MCHBAR32(MAD_CHNL) = 0x21;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100347 }
348}
349
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100350#define DEFAULT_TCK TCK_800MHZ
351
352unsigned int get_mem_min_tck(void)
353{
354 u32 reg32;
355 u8 rev;
356 const struct device *dev;
357 const struct northbridge_intel_sandybridge_config *cfg = NULL;
358
Angel Ponsb31d1d72020-01-10 01:35:09 +0100359 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100360 if (dev)
361 cfg = dev->chip_info;
362
363 /* If this is zero, it just means devicetree.cb didn't set it */
364 if (!cfg || cfg->max_mem_clock_mhz == 0) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100365
Julius Wernercd49cce2019-03-05 16:53:33 -0800366 if (CONFIG(NATIVE_RAMINIT_IGNORE_MAX_MEM_FUSES))
Patrick Rudolphb794a692017-08-08 13:13:51 +0200367 return TCK_1333MHZ;
368
Angel Ponsb31d1d72020-01-10 01:35:09 +0100369 rev = pci_read_config8(HOST_BRIDGE, PCI_DEVICE_ID);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100370
371 if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100372 /* Read Capabilities A Register DMFC bits */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100373 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100374 reg32 &= 0x7;
375
376 switch (reg32) {
377 case 7: return TCK_533MHZ;
378 case 6: return TCK_666MHZ;
379 case 5: return TCK_800MHZ;
Angel Pons7c49cb82020-03-16 23:17:32 +0100380 /* Reserved */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100381 default:
382 break;
383 }
384 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +0100385 /* Read Capabilities B Register DMFC bits */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100386 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_B);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100387 reg32 = (reg32 >> 4) & 0x7;
388
389 switch (reg32) {
390 case 7: return TCK_533MHZ;
391 case 6: return TCK_666MHZ;
392 case 5: return TCK_800MHZ;
393 case 4: return TCK_933MHZ;
394 case 3: return TCK_1066MHZ;
395 case 2: return TCK_1200MHZ;
396 case 1: return TCK_1333MHZ;
Angel Pons7c49cb82020-03-16 23:17:32 +0100397 /* Reserved */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100398 default:
399 break;
400 }
401 }
402 return DEFAULT_TCK;
403 } else {
404 if (cfg->max_mem_clock_mhz >= 1066)
405 return TCK_1066MHZ;
406 else if (cfg->max_mem_clock_mhz >= 933)
407 return TCK_933MHZ;
408 else if (cfg->max_mem_clock_mhz >= 800)
409 return TCK_800MHZ;
410 else if (cfg->max_mem_clock_mhz >= 666)
411 return TCK_666MHZ;
412 else if (cfg->max_mem_clock_mhz >= 533)
413 return TCK_533MHZ;
414 else
415 return TCK_400MHZ;
416 }
417}
418
419#define DEFAULT_PCI_MMIO_SIZE 2048
420
421static unsigned int get_mmio_size(void)
422{
423 const struct device *dev;
424 const struct northbridge_intel_sandybridge_config *cfg = NULL;
425
Angel Ponsb31d1d72020-01-10 01:35:09 +0100426 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100427 if (dev)
428 cfg = dev->chip_info;
429
430 /* If this is zero, it just means devicetree.cb didn't set it */
431 if (!cfg || cfg->pci_mmio_size == 0)
432 return DEFAULT_PCI_MMIO_SIZE;
433 else
434 return cfg->pci_mmio_size;
435}
436
Angel Pons88521882020-01-05 20:21:20 +0100437void dram_memorymap(ramctr_timing *ctrl, int me_uma_size)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100438{
Angel Pons7c49cb82020-03-16 23:17:32 +0100439 u32 reg, val, reclaim, tom, gfxstolen, gttsize;
440 size_t tsegbase, toludbase, remapbase, gfxstolenbase, mmiosize, gttbase;
441 size_t tsegsize, touudbase, remaplimit, mestolenbase, tsegbasedelta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100442 uint16_t ggc;
443
444 mmiosize = get_mmio_size();
445
Felix Held87ddea22020-01-26 04:55:27 +0100446 ggc = pci_read_config16(HOST_BRIDGE, GGC);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100447 if (!(ggc & 2)) {
448 gfxstolen = ((ggc >> 3) & 0x1f) * 32;
Angel Pons7c49cb82020-03-16 23:17:32 +0100449 gttsize = ((ggc >> 8) & 0x3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100450 } else {
451 gfxstolen = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100452 gttsize = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100453 }
454
455 tsegsize = CONFIG_SMM_TSEG_SIZE >> 20;
456
457 tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1];
458
459 mestolenbase = tom - me_uma_size;
460
Angel Pons7c49cb82020-03-16 23:17:32 +0100461 toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize, tom - me_uma_size);
462
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100463 gfxstolenbase = toludbase - gfxstolen;
464 gttbase = gfxstolenbase - gttsize;
465
466 tsegbase = gttbase - tsegsize;
467
Angel Pons7c49cb82020-03-16 23:17:32 +0100468 /* Round tsegbase down to nearest address aligned to tsegsize */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100469 tsegbasedelta = tsegbase & (tsegsize - 1);
470 tsegbase &= ~(tsegsize - 1);
471
472 gttbase -= tsegbasedelta;
473 gfxstolenbase -= tsegbasedelta;
474 toludbase -= tsegbasedelta;
475
Angel Pons7c49cb82020-03-16 23:17:32 +0100476 /* Test if it is possible to reclaim a hole in the RAM addressing */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100477 if (tom - me_uma_size > toludbase) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100478 /* Reclaim is possible */
479 reclaim = 1;
480 remapbase = MAX(4096, tom - me_uma_size);
481 remaplimit = remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1;
482 touudbase = remaplimit + 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100483 } else {
484 // Reclaim not possible
Angel Pons7c49cb82020-03-16 23:17:32 +0100485 reclaim = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100486 touudbase = tom - me_uma_size;
487 }
488
Angel Pons7c49cb82020-03-16 23:17:32 +0100489 /* Update memory map in PCIe configuration space */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100490 printk(BIOS_DEBUG, "Update PCI-E configuration space:\n");
491
Angel Pons7c49cb82020-03-16 23:17:32 +0100492 /* TOM (top of memory) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100493 reg = pci_read_config32(HOST_BRIDGE, TOM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100494 val = tom & 0xfff;
495 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100496 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100497 pci_write_config32(HOST_BRIDGE, TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100498
Angel Ponsb31d1d72020-01-10 01:35:09 +0100499 reg = pci_read_config32(HOST_BRIDGE, TOM + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100500 val = tom & 0xfffff000;
501 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100502 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100503 pci_write_config32(HOST_BRIDGE, TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100504
Angel Pons7c49cb82020-03-16 23:17:32 +0100505 /* TOLUD (Top Of Low Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100506 reg = pci_read_config32(HOST_BRIDGE, TOLUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100507 val = toludbase & 0xfff;
508 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100509 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOLUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100510 pci_write_config32(HOST_BRIDGE, TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100511
Angel Pons7c49cb82020-03-16 23:17:32 +0100512 /* TOUUD LSB (Top Of Upper Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100513 reg = pci_read_config32(HOST_BRIDGE, TOUUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100514 val = touudbase & 0xfff;
515 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100516 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100517 pci_write_config32(HOST_BRIDGE, TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100518
Angel Pons7c49cb82020-03-16 23:17:32 +0100519 /* TOUUD MSB */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100520 reg = pci_read_config32(HOST_BRIDGE, TOUUD + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100521 val = touudbase & 0xfffff000;
522 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100523 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100524 pci_write_config32(HOST_BRIDGE, TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100525
526 if (reclaim) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100527 /* REMAP BASE */
528 pci_write_config32(HOST_BRIDGE, REMAPBASE, remapbase << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100529 pci_write_config32(HOST_BRIDGE, REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100530
Angel Pons7c49cb82020-03-16 23:17:32 +0100531 /* REMAP LIMIT */
532 pci_write_config32(HOST_BRIDGE, REMAPLIMIT, remaplimit << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100533 pci_write_config32(HOST_BRIDGE, REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100534 }
Angel Pons7c49cb82020-03-16 23:17:32 +0100535 /* TSEG */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100536 reg = pci_read_config32(HOST_BRIDGE, TSEGMB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100537 val = tsegbase & 0xfff;
538 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100539 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TSEGMB, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100540 pci_write_config32(HOST_BRIDGE, TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100541
Angel Pons7c49cb82020-03-16 23:17:32 +0100542 /* GFX stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100543 reg = pci_read_config32(HOST_BRIDGE, BDSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100544 val = gfxstolenbase & 0xfff;
545 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100546 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BDSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100547 pci_write_config32(HOST_BRIDGE, BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100548
Angel Pons7c49cb82020-03-16 23:17:32 +0100549 /* GTT stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100550 reg = pci_read_config32(HOST_BRIDGE, BGSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100551 val = gttbase & 0xfff;
552 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100553 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BGSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100554 pci_write_config32(HOST_BRIDGE, BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100555
556 if (me_uma_size) {
Angel Ponsb31d1d72020-01-10 01:35:09 +0100557 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100558 val = (0x80000 - me_uma_size) & 0xfffff000;
559 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100560 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100561 pci_write_config32(HOST_BRIDGE, MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100562
Angel Pons7c49cb82020-03-16 23:17:32 +0100563 /* ME base */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100564 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100565 val = mestolenbase & 0xfff;
566 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held651f99f2019-12-30 16:28:48 +0100567 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100568 pci_write_config32(HOST_BRIDGE, MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100569
Angel Ponsb31d1d72020-01-10 01:35:09 +0100570 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100571 val = mestolenbase & 0xfffff000;
572 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100573 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100574 pci_write_config32(HOST_BRIDGE, MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100575
Angel Pons7c49cb82020-03-16 23:17:32 +0100576 /* ME mask */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100577 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100578 val = (0x80000 - me_uma_size) & 0xfff;
579 reg = (reg & ~0xfff00000) | (val << 20);
Angel Pons7c49cb82020-03-16 23:17:32 +0100580 reg = reg | ME_STLEN_EN; /* Set ME memory enable */
581 reg = reg | MELCK; /* Set lock bit on ME mem */
Felix Held651f99f2019-12-30 16:28:48 +0100582 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100583 pci_write_config32(HOST_BRIDGE, MESEG_MASK, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100584 }
585}
586
Angel Pons88521882020-01-05 20:21:20 +0100587static void wait_for_iosav(int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100588{
589 while (1) {
Angel Pons88521882020-01-05 20:21:20 +0100590 if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100591 return;
592 }
593}
594
Angel Pons88521882020-01-05 20:21:20 +0100595static void write_reset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100596{
597 int channel, slotrank;
598
Angel Pons7c49cb82020-03-16 23:17:32 +0100599 /* Choose a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100600 channel = (ctrl->rankmap[0]) ? 0 : 1;
601
Angel Pons88521882020-01-05 20:21:20 +0100602 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100603
Angel Pons7c49cb82020-03-16 23:17:32 +0100604 /* Choose a populated rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100605 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
606
607 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100608 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
609 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x80c01;
Angel Pons88521882020-01-05 20:21:20 +0100610 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100611 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100612
Angel Pons7c49cb82020-03-16 23:17:32 +0100613 /*
614 * Execute command queue - why is bit 22 set here?!
615 *
616 * This is actually using the IOSAV state machine as a timer, so refresh is allowed.
617 */
Angel Pons88521882020-01-05 20:21:20 +0100618 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = (1 << 22) | IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +0200619
Angel Pons88521882020-01-05 20:21:20 +0100620 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100621}
622
Angel Pons88521882020-01-05 20:21:20 +0100623void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100624{
Felix Held9fe248f2018-07-31 20:59:45 +0200625 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100626 int channel;
627
Angel Pons7c49cb82020-03-16 23:17:32 +0100628 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
629 ;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100630 do {
Angel Pons88521882020-01-05 20:21:20 +0100631 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100632 } while ((reg & 0x14) == 0);
633
Angel Pons7c49cb82020-03-16 23:17:32 +0100634 /* Set state of memory controller */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100635 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100636 MCHBAR32(MC_INIT_STATE_G) = reg;
637 MCHBAR32(MC_INIT_STATE) = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100638 reg |= 2; /* DDR reset */
Angel Pons88521882020-01-05 20:21:20 +0100639 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100640
Angel Pons7c49cb82020-03-16 23:17:32 +0100641 /* Assert DIMM reset signal */
642 MCHBAR32_AND(MC_INIT_STATE_G, ~2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100643
Angel Pons7c49cb82020-03-16 23:17:32 +0100644 /* Wait 200us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100645 udelay(200);
646
Angel Pons7c49cb82020-03-16 23:17:32 +0100647 /* Deassert DIMM reset signal */
Angel Pons88521882020-01-05 20:21:20 +0100648 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100649
Angel Pons7c49cb82020-03-16 23:17:32 +0100650 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100651 udelay(500);
652
Angel Pons7c49cb82020-03-16 23:17:32 +0100653 /* Enable DCLK */
Angel Pons88521882020-01-05 20:21:20 +0100654 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100655
Angel Pons7c49cb82020-03-16 23:17:32 +0100656 /* XXX Wait 20ns */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100657 udelay(1);
658
659 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100660 /* Set valid rank CKE */
Felix Held9fe248f2018-07-31 20:59:45 +0200661 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100662 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100663
Angel Pons7c49cb82020-03-16 23:17:32 +0100664 /* Wait 10ns for ranks to settle */
665 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100666
667 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100668 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100669
Angel Pons7c49cb82020-03-16 23:17:32 +0100670 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100671 write_reset(ctrl);
672 }
673}
674
675static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
676{
Angel Pons7c49cb82020-03-16 23:17:32 +0100677 /* Get ODT based on rankmap */
678 int dimms_per_ch = (ctrl->rankmap[channel] & 1) + ((ctrl->rankmap[channel] >> 2) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100679
680 if (dimms_per_ch == 1) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100681 return (const odtmap){60, 60};
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100682 } else {
683 return (const odtmap){120, 30};
684 }
685}
686
Angel Pons7c49cb82020-03-16 23:17:32 +0100687static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank, int reg, u32 val)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100688{
Angel Pons88521882020-01-05 20:21:20 +0100689 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100690
691 if (ctrl->rank_mirror[channel][slotrank]) {
692 /* DDR3 Rank1 Address mirror
Angel Pons7c49cb82020-03-16 23:17:32 +0100693 swap the following pins:
694 A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100695 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
Angel Pons7c49cb82020-03-16 23:17:32 +0100696 val = (val & ~0x1f8) | ((val >> 1) & 0xa8) | ((val & 0xa8) << 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100697 }
698
699 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100700 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f000;
701 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100702 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200703 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100704 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100705
706 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100707 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f000;
708 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +0100709 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200710 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100711 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100712
713 /* DRAM command MRS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100714 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x0f000;
715 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +0100716 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +0200717 (slotrank << 24) | (reg << 20) | val | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +0100718 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200719
Angel Pons7c49cb82020-03-16 23:17:32 +0100720 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100721 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100722}
723
Angel Pons88521882020-01-05 20:21:20 +0100724static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100725{
726 u16 mr0reg, mch_cas, mch_wr;
727 static const u8 mch_wr_t[12] = { 1, 2, 3, 4, 0, 5, 0, 6, 0, 7, 0, 0 };
Patrick Rudolph74203de2017-11-20 11:57:01 +0100728 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100729
730 /* DLL Reset - self clearing - set after CLK frequency has been changed */
731 mr0reg = 0x100;
732
Angel Pons7c49cb82020-03-16 23:17:32 +0100733 /* Convert CAS to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100734 if (ctrl->CAS < 12) {
735 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
736 } else {
737 mch_cas = (u16) (ctrl->CAS - 12);
738 mch_cas = ((mch_cas << 1) | 0x1);
739 }
740
Angel Pons7c49cb82020-03-16 23:17:32 +0100741 /* Convert tWR to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100742 mch_wr = mch_wr_t[ctrl->tWR - 5];
743
Angel Pons7c49cb82020-03-16 23:17:32 +0100744 mr0reg = (mr0reg & ~0x0004) | ((mch_cas & 0x1) << 2);
745 mr0reg = (mr0reg & ~0x0070) | ((mch_cas & 0xe) << 3);
746 mr0reg = (mr0reg & ~0x0e00) | (mch_wr << 9);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100747
Angel Pons7c49cb82020-03-16 23:17:32 +0100748 /* Precharge PD - Fast (desktop) 1 or slow (mobile) 0 - mostly power-saving feature */
749 mr0reg = (mr0reg & ~(1 << 12)) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100750 return mr0reg;
751}
752
753static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
754{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200755 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100756}
757
758static u32 encode_odt(u32 odt)
759{
760 switch (odt) {
761 case 30:
762 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
763 case 60:
764 return (1 << 2); // RZQ/4
765 case 120:
766 return (1 << 6); // RZQ/2
767 default:
768 case 0:
769 return 0;
770 }
771}
772
773static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
774{
775 odtmap odt;
776 u32 mr1reg;
777
778 odt = get_ODT(ctrl, rank, channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100779 mr1reg = 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100780
781 mr1reg |= encode_odt(odt.rttnom);
782
783 return mr1reg;
784}
785
786static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
787{
788 u16 mr1reg;
789
790 mr1reg = make_mr1(ctrl, rank, channel);
791
792 write_mrreg(ctrl, channel, rank, 1, mr1reg);
793}
794
795static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
796{
797 u16 pasr, cwl, mr2reg;
798 odtmap odt;
799 int srt;
800
801 pasr = 0;
802 cwl = ctrl->CWL - 5;
803 odt = get_ODT(ctrl, rank, channel);
804
805 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
806
807 mr2reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100808 mr2reg = (mr2reg & ~0x07) | pasr;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100809 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
810 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
811 mr2reg = (mr2reg & ~0x80) | (srt << 7);
812 mr2reg |= (odt.rttwr / 60) << 9;
813
814 write_mrreg(ctrl, channel, rank, 2, mr2reg);
815}
816
817static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
818{
819 write_mrreg(ctrl, channel, rank, 3, 0);
820}
821
Angel Pons88521882020-01-05 20:21:20 +0100822void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100823{
824 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100825 int channel;
826
827 FOR_ALL_POPULATED_CHANNELS {
828 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100829 /* MR2 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100830 dram_mr2(ctrl, slotrank, channel);
831
Angel Pons7c49cb82020-03-16 23:17:32 +0100832 /* MR3 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100833 dram_mr3(ctrl, slotrank, channel);
834
Angel Pons7c49cb82020-03-16 23:17:32 +0100835 /* MR1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100836 dram_mr1(ctrl, slotrank, channel);
837
Angel Pons7c49cb82020-03-16 23:17:32 +0100838 /* MR0 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100839 dram_mr0(ctrl, slotrank, channel);
840 }
841 }
842
843 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +0100844 MCHBAR32(IOSAV_n_SP_CMD_CTRL(0)) = 0x7;
845 MCHBAR32(IOSAV_n_SUBSEQ_CTRL(0)) = 0xf1001;
Angel Pons88521882020-01-05 20:21:20 +0100846 MCHBAR32(IOSAV_n_SP_CMD_ADDR(0)) = 0x60002;
Angel Pons7c49cb82020-03-16 23:17:32 +0100847 MCHBAR32(IOSAV_n_ADDR_UPDATE(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100848
849 /* DRAM command ZQCL */
Angel Pons7c49cb82020-03-16 23:17:32 +0100850 MCHBAR32(IOSAV_n_SP_CMD_CTRL(1)) = 0x1f003;
851 MCHBAR32(IOSAV_n_SUBSEQ_CTRL(1)) = 0x1901001;
Angel Pons88521882020-01-05 20:21:20 +0100852 MCHBAR32(IOSAV_n_SP_CMD_ADDR(1)) = 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +0100853 MCHBAR32(IOSAV_n_ADDR_UPDATE(1)) = 0x288;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100854
Angel Pons7c49cb82020-03-16 23:17:32 +0100855 /* Execute command queue on all channels. Do it four times. */
856 MCHBAR32(IOSAV_SEQ_CTL) = (1 << 18) | 4;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100857
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100858 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100859 /* Wait for ref drained */
Angel Pons88521882020-01-05 20:21:20 +0100860 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100861 }
862
Angel Pons7c49cb82020-03-16 23:17:32 +0100863 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +0100864 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100865
866 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +0100867 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100868
Angel Pons88521882020-01-05 20:21:20 +0100869 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100870
871 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
872
Angel Pons7c49cb82020-03-16 23:17:32 +0100873 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100874 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100875
876 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +0100877 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
878 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
879 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
880 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Felix Held9cf1dd22018-07-31 14:52:40 +0200881
Angel Pons7c49cb82020-03-16 23:17:32 +0100882 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +0100883 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100884
Angel Pons7c49cb82020-03-16 23:17:32 +0100885 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100886 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100887 }
888}
889
Felix Held3b906032020-01-14 17:05:43 +0100890static const u32 lane_base[] = {
891 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
892 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
893 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100894};
895
Angel Pons88521882020-01-05 20:21:20 +0100896void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100897{
Angel Pons88521882020-01-05 20:21:20 +0100898 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100899 int lane;
900 int slotrank, slot;
901 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +0100902 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100903
904 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +0100905 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
906 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100907 }
908
909 for (slot = 0; slot < NUM_SLOTS; slot++)
910 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
911 case 0:
912 default:
Angel Pons88521882020-01-05 20:21:20 +0100913 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100914 break;
915 case 1:
Angel Pons88521882020-01-05 20:21:20 +0100916 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100917 ctrl->timings[channel][2 * slot + 0].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100918 break;
919 case 2:
Angel Pons88521882020-01-05 20:21:20 +0100920 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100921 ctrl->timings[channel][2 * slot + 1].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100922 break;
923 case 3:
Angel Pons88521882020-01-05 20:21:20 +0100924 pi_coding_ctrl[slot] =
925 (ctrl->timings[channel][2 * slot].pi_coding +
Angel Pons7c49cb82020-03-16 23:17:32 +0100926 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100927 break;
928 }
929
Angel Pons7c49cb82020-03-16 23:17:32 +0100930 /* Enable CMD XOVER */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100931 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons7c49cb82020-03-16 23:17:32 +0100932 reg32 |= (pi_coding_ctrl[0] & 0x3f) << 6;
933 reg32 |= (pi_coding_ctrl[0] & 0x40) << 9;
Angel Pons88521882020-01-05 20:21:20 +0100934 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100935 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
936
Angel Pons88521882020-01-05 20:21:20 +0100937 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100938
Angel Pons7c49cb82020-03-16 23:17:32 +0100939 /* Enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +0100940 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
941 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100942
943 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100944 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Angel Pons88521882020-01-05 20:21:20 +0100945 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100946 if (shift < 0)
947 shift = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100948
Angel Pons88521882020-01-05 20:21:20 +0100949 offset_pi_code = ctrl->pi_code_offset + shift;
Angel Pons7c49cb82020-03-16 23:17:32 +0100950
951 /* Set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +0100952 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
953 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100954 }
955
Angel Pons88521882020-01-05 20:21:20 +0100956 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
957 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100958
Angel Pons88521882020-01-05 20:21:20 +0100959 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +0100960 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100961
Angel Pons88521882020-01-05 20:21:20 +0100962 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100963
964 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100965 int post_timA_min_high = 7, pre_timA_min_high = 7;
966 int post_timA_max_high = 0, pre_timA_max_high = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100967 int shift_402x = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100968 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100969
970 if (shift < 0)
971 shift = 0;
972
973 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +0200974 post_timA_min_high = MIN(post_timA_min_high,
975 (ctrl->timings[channel][slotrank].lanes[lane].
976 timA + shift) >> 6);
977 pre_timA_min_high = MIN(pre_timA_min_high,
978 ctrl->timings[channel][slotrank].lanes[lane].
979 timA >> 6);
980 post_timA_max_high = MAX(post_timA_max_high,
981 (ctrl->timings[channel][slotrank].lanes[lane].
982 timA + shift) >> 6);
983 pre_timA_max_high = MAX(pre_timA_max_high,
984 ctrl->timings[channel][slotrank].lanes[lane].
985 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100986 }
987
988 if (pre_timA_max_high - pre_timA_min_high <
989 post_timA_max_high - post_timA_min_high)
990 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +0100991
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100992 else if (pre_timA_max_high - pre_timA_min_high >
993 post_timA_max_high - post_timA_min_high)
994 shift_402x = -1;
995
Felix Helddee167e2019-12-30 17:30:16 +0100996 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +0100997 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100998 post_timA_min_high) << (4 * slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +0100999
Angel Pons88521882020-01-05 20:21:20 +01001000 reg_roundtrip_latency |=
1001 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001002 shift_402x) << (8 * slotrank);
1003
1004 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001005 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001006 (((ctrl->timings[channel][slotrank].lanes[lane].
1007 timA + shift) & 0x3f)
1008 |
1009 ((ctrl->timings[channel][slotrank].lanes[lane].
1010 rising + shift) << 8)
1011 |
1012 (((ctrl->timings[channel][slotrank].lanes[lane].
1013 timA + shift -
1014 (post_timA_min_high << 6)) & 0x1c0) << 10)
1015 | ((ctrl->timings[channel][slotrank].lanes[lane].
1016 falling + shift) << 20));
1017
Felix Heldfb19c8a2020-01-14 21:27:59 +01001018 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001019 (((ctrl->timings[channel][slotrank].lanes[lane].
1020 timC + shift) & 0x3f)
1021 |
1022 (((ctrl->timings[channel][slotrank].lanes[lane].
1023 timB + shift) & 0x3f) << 8)
1024 |
1025 (((ctrl->timings[channel][slotrank].lanes[lane].
1026 timB + shift) & 0x1c0) << 9)
1027 |
1028 (((ctrl->timings[channel][slotrank].lanes[lane].
1029 timC + shift) & 0x40) << 13));
1030 }
1031 }
Angel Pons88521882020-01-05 20:21:20 +01001032 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1033 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001034}
1035
Angel Pons88521882020-01-05 20:21:20 +01001036static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001037{
Angel Pons88521882020-01-05 20:21:20 +01001038 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001039
1040 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001041 write MR3 MPR enable
1042 in this mode only RD and RDA are allowed
1043 all reads return a predefined pattern */
1044 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1045 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = (0xc01 | (ctrl->tMOD << 16));
Angel Pons88521882020-01-05 20:21:20 +01001046 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001047 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001048
1049 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001050 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1051 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4040c01;
Angel Pons88521882020-01-05 20:21:20 +01001052 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001053 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001054
1055 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001056 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1057 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x100f | ((ctrl->CAS + 36) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001058 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001059 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001060
1061 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001062 write MR3 MPR disable */
1063 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1064 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001065 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001066 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001067
Angel Pons7c49cb82020-03-16 23:17:32 +01001068 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001069 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001070
Angel Pons88521882020-01-05 20:21:20 +01001071 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001072}
1073
Angel Pons7c49cb82020-03-16 23:17:32 +01001074static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank, int lane)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001075{
1076 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Angel Pons7c49cb82020-03-16 23:17:32 +01001077
1078 return (MCHBAR32(lane_base[lane] +
1079 GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >> (timA % 32)) & 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001080}
1081
1082struct run {
1083 int middle;
1084 int end;
1085 int start;
1086 int all;
1087 int length;
1088};
1089
1090static struct run get_longest_zero_run(int *seq, int sz)
1091{
1092 int i, ls;
1093 int bl = 0, bs = 0;
1094 struct run ret;
1095
1096 ls = 0;
1097 for (i = 0; i < 2 * sz; i++)
1098 if (seq[i % sz]) {
1099 if (i - ls > bl) {
1100 bl = i - ls;
1101 bs = ls;
1102 }
1103 ls = i + 1;
1104 }
1105 if (bl == 0) {
1106 ret.middle = sz / 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001107 ret.start = 0;
1108 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001109 ret.length = sz;
Angel Pons7c49cb82020-03-16 23:17:32 +01001110 ret.all = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001111 return ret;
1112 }
1113
Angel Pons7c49cb82020-03-16 23:17:32 +01001114 ret.start = bs % sz;
1115 ret.end = (bs + bl - 1) % sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001116 ret.middle = (bs + (bl - 1) / 2) % sz;
1117 ret.length = bl;
Angel Pons7c49cb82020-03-16 23:17:32 +01001118 ret.all = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001119
1120 return ret;
1121}
1122
Angel Pons7c49cb82020-03-16 23:17:32 +01001123static void discover_timA_coarse(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001124{
1125 int timA;
1126 int statistics[NUM_LANES][128];
1127 int lane;
1128
1129 for (timA = 0; timA < 128; timA++) {
1130 FOR_ALL_LANES {
1131 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1132 }
1133 program_timings(ctrl, channel);
1134
1135 test_timA(ctrl, channel, slotrank);
1136
1137 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001138 statistics[lane][timA] = !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001139 }
1140 }
1141 FOR_ALL_LANES {
1142 struct run rn = get_longest_zero_run(statistics[lane], 128);
1143 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1144 upperA[lane] = rn.end;
1145 if (upperA[lane] < rn.middle)
1146 upperA[lane] += 128;
Angel Pons7c49cb82020-03-16 23:17:32 +01001147
Patrick Rudolph368b6152016-11-25 16:36:52 +01001148 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001149 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001150 }
1151}
1152
Angel Pons7c49cb82020-03-16 23:17:32 +01001153static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001154{
1155 int timA_delta;
1156 int statistics[NUM_LANES][51];
1157 int lane, i;
1158
1159 memset(statistics, 0, sizeof(statistics));
1160
1161 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01001162
1163 FOR_ALL_LANES {
1164 ctrl->timings[channel][slotrank].lanes[lane].timA
1165 = upperA[lane] + timA_delta + 0x40;
1166 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001167 program_timings(ctrl, channel);
1168
1169 for (i = 0; i < 100; i++) {
1170 test_timA(ctrl, channel, slotrank);
1171 FOR_ALL_LANES {
1172 statistics[lane][timA_delta + 25] +=
Angel Pons7c49cb82020-03-16 23:17:32 +01001173 does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001174 }
1175 }
1176 }
1177 FOR_ALL_LANES {
1178 int last_zero, first_all;
1179
1180 for (last_zero = -25; last_zero <= 25; last_zero++)
1181 if (statistics[lane][last_zero + 25])
1182 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01001183
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001184 last_zero--;
1185 for (first_all = -25; first_all <= 25; first_all++)
1186 if (statistics[lane][first_all + 25] == 100)
1187 break;
1188
Angel Pons7c49cb82020-03-16 23:17:32 +01001189 printram("lane %d: %d, %d\n", lane, last_zero, first_all);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001190
1191 ctrl->timings[channel][slotrank].lanes[lane].timA =
Angel Pons7c49cb82020-03-16 23:17:32 +01001192 (last_zero + first_all) / 2 + upperA[lane];
1193
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001194 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01001195 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001196 }
1197}
1198
Angel Pons891f2bc2020-01-10 01:27:28 +01001199static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001200{
1201 int works[NUM_LANES];
1202 int lane;
Angel Pons7c49cb82020-03-16 23:17:32 +01001203
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001204 while (1) {
1205 int all_works = 1, some_works = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001206
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001207 program_timings(ctrl, channel);
1208 test_timA(ctrl, channel, slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001209
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001210 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001211 works[lane] = !does_lane_work(ctrl, channel, slotrank, lane);
1212
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001213 if (works[lane])
1214 some_works = 1;
1215 else
1216 all_works = 0;
1217 }
1218 if (all_works)
1219 return 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001220
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001221 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001222 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001223 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1224 channel, slotrank);
1225 return MAKE_ERR;
1226 }
Angel Pons88521882020-01-05 20:21:20 +01001227 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001228 printram("4024 -= 2;\n");
1229 continue;
1230 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001231 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001232 printram("4028 += 2;\n");
Angel Pons7c49cb82020-03-16 23:17:32 +01001233
Felix Heldef4fe3e2019-12-31 14:15:05 +01001234 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001235 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1236 channel, slotrank);
1237 return MAKE_ERR;
1238 }
1239 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001240 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001241 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001242 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001243 }
1244 }
1245 return 0;
1246}
1247
1248struct timA_minmax {
1249 int timA_min_high, timA_max_high;
1250};
1251
Angel Pons88521882020-01-05 20:21:20 +01001252static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001253 struct timA_minmax *mnmx)
1254{
1255 int lane;
1256 mnmx->timA_min_high = 7;
1257 mnmx->timA_max_high = 0;
1258
1259 FOR_ALL_LANES {
1260 if (mnmx->timA_min_high >
1261 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1262 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001263 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001264 if (mnmx->timA_max_high <
1265 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1266 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001267 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001268 }
1269}
1270
Angel Pons88521882020-01-05 20:21:20 +01001271static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001272 struct timA_minmax *mnmx)
1273{
1274 struct timA_minmax post;
1275 int shift_402x = 0;
1276
Angel Pons7c49cb82020-03-16 23:17:32 +01001277 /* Get changed maxima */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001278 pre_timA_change(ctrl, channel, slotrank, &post);
1279
1280 if (mnmx->timA_max_high - mnmx->timA_min_high <
1281 post.timA_max_high - post.timA_min_high)
1282 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001283
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001284 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1285 post.timA_max_high - post.timA_min_high)
1286 shift_402x = -1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001287
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001288 else
1289 shift_402x = 0;
1290
Felix Heldef4fe3e2019-12-31 14:15:05 +01001291 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001292 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001293 printram("4024 += %d;\n", shift_402x);
1294 printram("4028 += %d;\n", shift_402x);
1295}
1296
Angel Pons7c49cb82020-03-16 23:17:32 +01001297/*
1298 * Compensate the skew between DQS and DQs.
1299 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001300 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1301 * The controller has to measure and compensate this skew for every byte-lane. By delaying
Angel Pons7c49cb82020-03-16 23:17:32 +01001302 * either all DQ signals or DQS signal, a full phase shift can be introduced. It is assumed
Angel Pons891f2bc2020-01-10 01:27:28 +01001303 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001304 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001305 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1306 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1307 * over all possible values to do a full phase shift and issues read commands. With DQS and
Angel Pons7c49cb82020-03-16 23:17:32 +01001308 * DQ in phase the data being read is expected to alternate on every byte:
1309 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001310 * 0xFF 0x00 0xFF ...
Angel Pons7c49cb82020-03-16 23:17:32 +01001311 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001312 * Once the controller has detected this pattern a bit in the result register is set for the
1313 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001314 */
Angel Pons88521882020-01-05 20:21:20 +01001315int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001316{
1317 int channel, slotrank, lane;
1318 int err;
1319
1320 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1321 int all_high, some_high;
1322 int upperA[NUM_LANES];
1323 struct timA_minmax mnmx;
1324
Angel Pons88521882020-01-05 20:21:20 +01001325 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001326
Felix Held2bb3cdf2018-07-28 00:23:59 +02001327 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001328 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1329 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001330 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001331 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001332
Angel Pons7c49cb82020-03-16 23:17:32 +01001333 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001334 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001335
Angel Pons88521882020-01-05 20:21:20 +01001336 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001337
Felix Heldef4fe3e2019-12-31 14:15:05 +01001338 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001339 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001340 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001341
Felix Held2bb3cdf2018-07-28 00:23:59 +02001342 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001343
Felix Held2bb3cdf2018-07-28 00:23:59 +02001344 all_high = 1;
1345 some_high = 0;
1346 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001347 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001348 some_high = 1;
1349 else
1350 all_high = 0;
1351 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001352
1353 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001354 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001355 printram("4028--;\n");
1356 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001357 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001358 upperA[lane] -= 0x40;
1359
1360 }
1361 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001362 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001363 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001364 printram("4024++;\n");
1365 printram("4028++;\n");
1366 }
1367
1368 program_timings(ctrl, channel);
1369
1370 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1371
1372 err = discover_402x(ctrl, channel, slotrank, upperA);
1373 if (err)
1374 return err;
1375
1376 post_timA_change(ctrl, channel, slotrank, &mnmx);
1377 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1378
1379 discover_timA_fine(ctrl, channel, slotrank, upperA);
1380
1381 post_timA_change(ctrl, channel, slotrank, &mnmx);
1382 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1383
1384 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001385 ctrl->timings[channel][slotrank].lanes[lane].timA -=
1386 mnmx.timA_min_high * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001387 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001388 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001389 printram("4028 -= %d;\n", mnmx.timA_min_high);
1390
1391 post_timA_change(ctrl, channel, slotrank, &mnmx);
1392
1393 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001394 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001395 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001396
1397 printram("final results:\n");
1398 FOR_ALL_LANES
Angel Pons7c49cb82020-03-16 23:17:32 +01001399 printram("Aval: %d, %d, %d: %x\n", channel, slotrank, lane,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001400 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001401
Angel Pons88521882020-01-05 20:21:20 +01001402 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001403
1404 toggle_io_reset();
1405 }
1406
1407 FOR_ALL_POPULATED_CHANNELS {
1408 program_timings(ctrl, channel);
1409 }
1410 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001411 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001412 }
1413 return 0;
1414}
1415
Angel Pons88521882020-01-05 20:21:20 +01001416static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001417{
1418 int lane;
1419
1420 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001421 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1422 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001423 }
1424
Angel Pons88521882020-01-05 20:21:20 +01001425 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001426
1427 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001428 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
1429 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
1430 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | 4 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001431 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | (6 << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +01001432 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001433
1434 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001435 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f207;
1436 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8041001;
Angel Pons88521882020-01-05 20:21:20 +01001437 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001438 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001439
1440 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01001441 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f201;
1442 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x80411f4;
Angel Pons88521882020-01-05 20:21:20 +01001443 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001444 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001445
1446 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001447 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f207;
1448 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
1449 0x08000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001450 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001451 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001452
Angel Pons7c49cb82020-03-16 23:17:32 +01001453 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001454 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001455
Angel Pons88521882020-01-05 20:21:20 +01001456 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001457
1458 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001459 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1460 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001461 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001462 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001463
1464 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001465 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f006;
1466 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
1467 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) | 8 | (ctrl->CAS << 16);
Angel Pons88521882020-01-05 20:21:20 +01001468 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001469 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001470
1471 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001472 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1473 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01001474 0x40011f4 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001475 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001476 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001477
1478 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001479 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
1480 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001481 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001482 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001483
Angel Pons7c49cb82020-03-16 23:17:32 +01001484 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001485 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001486
Angel Pons88521882020-01-05 20:21:20 +01001487 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001488}
1489
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001490static void timC_threshold_process(int *data, const int count)
1491{
1492 int min = data[0];
1493 int max = min;
1494 int i;
1495 for (i = 1; i < count; i++) {
1496 if (min > data[i])
1497 min = data[i];
Angel Pons7c49cb82020-03-16 23:17:32 +01001498
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001499 if (max < data[i])
1500 max = data[i];
1501 }
Angel Pons7c49cb82020-03-16 23:17:32 +01001502 int threshold = min / 2 + max / 2;
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001503 for (i = 0; i < count; i++)
1504 data[i] = data[i] > threshold;
Angel Pons7c49cb82020-03-16 23:17:32 +01001505
Angel Pons891f2bc2020-01-10 01:27:28 +01001506 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001507}
1508
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001509static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1510{
1511 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01001512 int stats[NUM_LANES][MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001513 int lane;
1514
Angel Pons88521882020-01-05 20:21:20 +01001515 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001516
1517 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001518 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1519 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01001520 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001521 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Felix Held9cf1dd22018-07-31 14:52:40 +02001522
Angel Pons7c49cb82020-03-16 23:17:32 +01001523 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001524 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001525
1526 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001527 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001528 program_timings(ctrl, channel);
1529
1530 test_timC(ctrl, channel, slotrank);
1531
1532 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001533 stats[lane][timC] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001534 }
1535 }
1536 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001537 struct run rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1538
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001539 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001540 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1541 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001542 /*
1543 * With command training not being done yet, the lane can be erroneous.
1544 * Take the average as reference and try again to find a run.
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001545 */
Angel Pons7c49cb82020-03-16 23:17:32 +01001546 timC_threshold_process(stats[lane], ARRAY_SIZE(stats[lane]));
1547 rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1548
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001549 if (rn.all || rn.length < 8) {
1550 printk(BIOS_EMERG, "timC recovery failed\n");
1551 return MAKE_ERR;
1552 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001553 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001554 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001555 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001556 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001557 }
1558 return 0;
1559}
1560
Angel Pons88521882020-01-05 20:21:20 +01001561static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001562{
1563 int channel, ret = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001564
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001565 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1566 ret++;
Angel Pons7c49cb82020-03-16 23:17:32 +01001567
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001568 return ret;
1569}
1570
Angel Pons88521882020-01-05 20:21:20 +01001571static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001572{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301573 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001574 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Angel Pons7c49cb82020-03-16 23:17:32 +01001575
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001576 for (j = 0; j < 16; j++)
1577 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
Angel Pons7c49cb82020-03-16 23:17:32 +01001578
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001579 sfence();
1580}
1581
Angel Pons88521882020-01-05 20:21:20 +01001582static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001583{
1584 int ret = 0;
1585 int channel;
1586 FOR_ALL_POPULATED_CHANNELS ret++;
1587 return ret;
1588}
1589
Angel Pons88521882020-01-05 20:21:20 +01001590static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001591{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301592 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001593 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301594 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01001595
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001596 for (j = 0; j < 16; j++)
1597 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
Angel Pons7c49cb82020-03-16 23:17:32 +01001598
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001599 for (j = 0; j < 16; j++)
1600 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
Angel Pons7c49cb82020-03-16 23:17:32 +01001601
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001602 sfence();
1603}
1604
Angel Pons88521882020-01-05 20:21:20 +01001605static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001606{
1607 int channel, slotrank, lane;
1608
1609 FOR_ALL_POPULATED_CHANNELS {
1610 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001611 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1612 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001613 }
1614
1615 program_timings(ctrl, channel);
1616
1617 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001618 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001619
1620 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001621 write MR3 MPR enable
1622 in this mode only RD and RDA are allowed
1623 all reads return a predefined pattern */
1624 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1625 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001626 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001627 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001628 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001629 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001630
1631 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001632 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1633 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001634 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001635 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001636
1637 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001638 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1639 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001640 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001641 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001642 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001643 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001644
1645 /* DRAM command MRS
1646 * write MR3 MPR disable */
Angel Pons7c49cb82020-03-16 23:17:32 +01001647 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1648 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001649 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001650 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001651 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001652 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02001653
Angel Pons7c49cb82020-03-16 23:17:32 +01001654 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001655 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001656
Angel Pons88521882020-01-05 20:21:20 +01001657 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001658 }
1659
1660 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001661 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1662 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001663 }
1664
1665 program_timings(ctrl, channel);
1666
1667 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001668 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001669 /* DRAM command MRS
1670 * write MR3 MPR enable
1671 * in this mode only RD and RDA are allowed
1672 * all reads return a predefined pattern */
Angel Pons7c49cb82020-03-16 23:17:32 +01001673 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
1674 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001675 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001676 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001677 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01001678 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001679
1680 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001681 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
1682 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01001683 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01001684 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001685
1686 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001687 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
1688 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001689 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001690 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001691 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001692 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001693
1694 /* DRAM command MRS
1695 * write MR3 MPR disable */
Angel Pons7c49cb82020-03-16 23:17:32 +01001696 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
1697 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001698 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001699 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001700 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001701 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001702
Angel Pons7c49cb82020-03-16 23:17:32 +01001703 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001704 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001705
Angel Pons88521882020-01-05 20:21:20 +01001706 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001707 }
1708 }
1709}
1710
Angel Pons88521882020-01-05 20:21:20 +01001711static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001712{
1713 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001714 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001715
Angel Pons88521882020-01-05 20:21:20 +01001716 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001717 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001718 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f207;
1719 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001720 0x8000c01 | ((ctrl->CWL + ctrl->tWLO) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001721 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 8 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001722 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001723
1724 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001725 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f107;
1726 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4000c01 | ((ctrl->CAS + 38) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001727 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01001728 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001729
Angel Pons7c49cb82020-03-16 23:17:32 +01001730 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001731 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001732
Angel Pons88521882020-01-05 20:21:20 +01001733 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001734
1735 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001736 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001737}
1738
1739static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1740{
1741 int timB;
1742 int statistics[NUM_LANES][128];
1743 int lane;
1744
Angel Pons88521882020-01-05 20:21:20 +01001745 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001746
1747 for (timB = 0; timB < 128; timB++) {
1748 FOR_ALL_LANES {
1749 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1750 }
1751 program_timings(ctrl, channel);
1752
1753 test_timB(ctrl, channel, slotrank);
1754
1755 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001756 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1757 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1758 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001759 }
1760 }
1761 FOR_ALL_LANES {
1762 struct run rn = get_longest_zero_run(statistics[lane], 128);
Angel Pons7c49cb82020-03-16 23:17:32 +01001763 /*
1764 * timC is a direct function of timB's 6 LSBs. Some tests increments the value
1765 * of timB by a small value, which might cause the 6-bit value to overflow if
1766 * it's close to 0x3f. Increment the value by a small offset if it's likely
1767 * to overflow, to make sure it won't overflow while running tests and bricks
1768 * the system due to a non matching timC.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001769 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001770 * TODO: find out why some tests (edge write discovery) increment timB.
1771 */
1772 if ((rn.start & 0x3f) == 0x3e)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001773 rn.start += 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001774 else if ((rn.start & 0x3f) == 0x3f)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001775 rn.start += 1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001776
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001777 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1778 if (rn.all) {
1779 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1780 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001781
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001782 return MAKE_ERR;
1783 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001784 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1785 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001786 }
1787 return 0;
1788}
1789
1790static int get_timB_high_adjust(u64 val)
1791{
1792 int i;
1793
1794 /* good */
1795 if (val == 0xffffffffffffffffLL)
1796 return 0;
1797
1798 if (val >= 0xf000000000000000LL) {
1799 /* needs negative adjustment */
1800 for (i = 0; i < 8; i++)
1801 if (val << (8 * (7 - i) + 4))
1802 return -i;
1803 } else {
1804 /* needs positive adjustment */
1805 for (i = 0; i < 8; i++)
1806 if (val >> (8 * (7 - i) + 4))
1807 return i;
1808 }
1809 return 8;
1810}
1811
Angel Pons88521882020-01-05 20:21:20 +01001812static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001813{
1814 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001815 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001816 FOR_ALL_POPULATED_CHANNELS {
1817 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001818 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001819 }
1820 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1821
Angel Pons88521882020-01-05 20:21:20 +01001822 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001823
Angel Pons88521882020-01-05 20:21:20 +01001824 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001825
1826 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001827 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
1828 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01001829 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001830 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001831
1832 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001833 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f207;
1834 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8040c01;
Angel Pons88521882020-01-05 20:21:20 +01001835 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001836 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001837
1838 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01001839 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f201;
1840 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x8041003;
Angel Pons88521882020-01-05 20:21:20 +01001841 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01001842 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x3e2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001843
1844 /* DRAM command NOP */
Angel Pons7c49cb82020-03-16 23:17:32 +01001845 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f207;
1846 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02001847 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001848 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x8;
Angel Pons7c49cb82020-03-16 23:17:32 +01001849 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001850
Angel Pons7c49cb82020-03-16 23:17:32 +01001851 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001852 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001853
Angel Pons88521882020-01-05 20:21:20 +01001854 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001855
1856 /* DRAM command PREA */
Angel Pons7c49cb82020-03-16 23:17:32 +01001857 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f002;
1858 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | ((ctrl->tRP) << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01001859 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01001860 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001861
1862 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001863 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f006;
1864 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0xc01 | ((ctrl->tRCD) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001865 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001866 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001867
1868 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01001869 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x3f105;
1870 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x4000c01 | ((ctrl->tRP +
Angel Pons88521882020-01-05 20:21:20 +01001871 ctrl->timings[channel][slotrank].roundtrip_latency +
Felix Heldef4fe3e2019-12-31 14:15:05 +01001872 ctrl->timings[channel][slotrank].io_latency) << 16);
Angel Pons88521882020-01-05 20:21:20 +01001873 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60008;
Angel Pons7c49cb82020-03-16 23:17:32 +01001874 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001875
Angel Pons7c49cb82020-03-16 23:17:32 +01001876 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001877 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3);
Felix Held9cf1dd22018-07-31 14:52:40 +02001878
Angel Pons88521882020-01-05 20:21:20 +01001879 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001880 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001881 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01001882 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01001883 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001884 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
1885 ctrl->timings[channel][slotrank].lanes[lane].timB +=
1886 get_timB_high_adjust(res) * 64;
1887
1888 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01001889 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
1890 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001891 }
1892 }
Angel Pons88521882020-01-05 20:21:20 +01001893 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001894}
1895
Angel Pons88521882020-01-05 20:21:20 +01001896static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001897{
1898 int slotrank;
1899
Angel Pons88521882020-01-05 20:21:20 +01001900 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001901
1902 /* choose an existing rank. */
1903 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
1904
1905 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01001906 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
1907 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01001908 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001909 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001910
Angel Pons7c49cb82020-03-16 23:17:32 +01001911 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001912 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001913
Angel Pons88521882020-01-05 20:21:20 +01001914 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001915}
1916
Angel Pons7c49cb82020-03-16 23:17:32 +01001917/*
1918 * Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001919 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001920 * Since DDR3 uses a fly-by topology, the data and strobes signals reach the chips at different
1921 * times with respect to command, address and clock signals. By delaying either all DQ/DQS or
1922 * all CMD/ADDR/CLK signals, a full phase shift can be introduced. It is assumed that the
1923 * CLK/ADDR/CMD signals have the same routing delay.
1924 *
1925 * To find the required phase shift the DRAM is placed in "write leveling" mode. In this mode,
1926 * the DRAM-chip samples the CLK on every DQS edge and feeds back the sampled value on the data
1927 * lanes (DQ).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001928 */
Angel Pons88521882020-01-05 20:21:20 +01001929int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001930{
1931 int channel, slotrank, lane;
1932 int err;
1933
1934 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001935 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001936
1937 FOR_ALL_POPULATED_CHANNELS {
1938 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001939 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001940 }
1941
Angel Pons7c49cb82020-03-16 23:17:32 +01001942 /* Refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01001943 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001944 FOR_ALL_POPULATED_CHANNELS {
1945 write_op(ctrl, channel);
1946 }
1947
Angel Pons7c49cb82020-03-16 23:17:32 +01001948 /* Enable write leveling on all ranks
1949 Disable all DQ outputs
1950 Only NOP is allowed in this mode */
1951 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
1952 write_mrreg(ctrl, channel, slotrank, 1,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001953 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001954
Angel Pons88521882020-01-05 20:21:20 +01001955 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001956
1957 toggle_io_reset();
1958
Angel Pons7c49cb82020-03-16 23:17:32 +01001959 /* Set any valid value for timB, it gets corrected later */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001960 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1961 err = discover_timB(ctrl, channel, slotrank);
1962 if (err)
1963 return err;
1964 }
1965
Angel Pons7c49cb82020-03-16 23:17:32 +01001966 /* Disable write leveling on all ranks */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001967 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
Angel Pons7c49cb82020-03-16 23:17:32 +01001968 write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001969
Angel Pons88521882020-01-05 20:21:20 +01001970 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001971
1972 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001973 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001974
Angel Pons7c49cb82020-03-16 23:17:32 +01001975 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01001976 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001977
1978 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001979 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
1980 MCHBAR32(IOSAV_STATUS_ch(channel));
1981 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001982
1983 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01001984 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
1985 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x659001;
Angel Pons88521882020-01-05 20:21:20 +01001986 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01001987 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001988
Angel Pons7c49cb82020-03-16 23:17:32 +01001989 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01001990 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001991
Angel Pons88521882020-01-05 20:21:20 +01001992 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001993 }
1994
1995 toggle_io_reset();
1996
1997 printram("CPE\n");
1998 precharge(ctrl);
1999 printram("CPF\n");
2000
2001 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002002 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002003 }
2004
2005 FOR_ALL_POPULATED_CHANNELS {
2006 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01002007 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002008 }
2009
2010 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2011 err = discover_timC(ctrl, channel, slotrank);
2012 if (err)
2013 return err;
2014 }
2015
2016 FOR_ALL_POPULATED_CHANNELS
2017 program_timings(ctrl, channel);
2018
2019 /* measure and adjust timB timings */
2020 adjust_high_timB(ctrl);
2021
2022 FOR_ALL_POPULATED_CHANNELS
2023 program_timings(ctrl, channel);
2024
2025 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002026 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002027 }
2028 return 0;
2029}
2030
Angel Pons88521882020-01-05 20:21:20 +01002031static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002032{
2033 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2034 int timC_delta;
2035 int lanes_ok = 0;
2036 int ctr = 0;
2037 int lane;
2038
2039 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2040 FOR_ALL_LANES {
2041 ctrl->timings[channel][slotrank].lanes[lane].timC =
2042 saved_rt.lanes[lane].timC + timC_delta;
2043 }
2044 program_timings(ctrl, channel);
2045 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002046 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002047 }
2048
Angel Pons88521882020-01-05 20:21:20 +01002049 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002050
Angel Pons88521882020-01-05 20:21:20 +01002051 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002052 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002053 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
2054 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002055 ((MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10)
Felix Held2bb3cdf2018-07-28 00:23:59 +02002056 | 8 | (ctrl->tRCD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002057 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002058 (slotrank << 24) | ctr | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002059 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x244;
Felix Held9fe248f2018-07-31 20:59:45 +02002060
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002061 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002062 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2063 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002064 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002065 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = (slotrank << 24);
2066 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
Angel Pons7c49cb82020-03-16 23:17:32 +01002067 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002068
2069 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002070 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2071 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002072 0x4001020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002073 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24);
2074 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
Angel Pons7c49cb82020-03-16 23:17:32 +01002075 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x20e42;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002076
2077 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002078 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2079 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xf1001;
Angel Pons88521882020-01-05 20:21:20 +01002080 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002081 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002082
Angel Pons7c49cb82020-03-16 23:17:32 +01002083 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002084 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002085
Angel Pons88521882020-01-05 20:21:20 +01002086 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002087 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002088 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002089
2090 if (r32 == 0)
2091 lanes_ok |= 1 << lane;
2092 }
2093 ctr++;
2094 if (lanes_ok == ((1 << NUM_LANES) - 1))
2095 break;
2096 }
2097
2098 ctrl->timings[channel][slotrank] = saved_rt;
2099
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002100 return lanes_ok != ((1 << NUM_LANES) - 1);
2101}
2102
Angel Pons88521882020-01-05 20:21:20 +01002103static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002104{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302105 unsigned int i, j;
Angel Pons7c49cb82020-03-16 23:17:32 +01002106 unsigned int offset = get_precedening_channels(ctrl, channel) * 0x40;
2107 unsigned int step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002108
2109 if (patno) {
2110 u8 base8 = 0x80 >> ((patno - 1) % 8);
2111 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2112 for (i = 0; i < 32; i++) {
2113 for (j = 0; j < 16; j++) {
2114 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01002115
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002116 if (invert[patno - 1][i] & (1 << (j / 2)))
2117 val = ~val;
Angel Pons7c49cb82020-03-16 23:17:32 +01002118
2119 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002120 }
2121 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002122 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002123 for (i = 0; i < ARRAY_SIZE(pattern); i++) {
2124 for (j = 0; j < 16; j++) {
2125 const u32 val = pattern[i][j];
2126 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
2127 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002128 }
2129 sfence();
2130 }
2131}
2132
Angel Pons88521882020-01-05 20:21:20 +01002133static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002134{
2135 int channel, slotrank;
2136
2137 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002138 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002139
Angel Pons7c49cb82020-03-16 23:17:32 +01002140 /* Choose an existing rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002141 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2142
2143 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01002144 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
2145 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002146 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002147 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002148
Angel Pons7c49cb82020-03-16 23:17:32 +01002149 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002150 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002151
Angel Pons88521882020-01-05 20:21:20 +01002152 wait_for_iosav(channel);
2153 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002154 }
2155
2156 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002157 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002158 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002159 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002160
2161 /* choose an existing rank. */
2162 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2163
2164 /* DRAM command ZQCS */
Angel Pons7c49cb82020-03-16 23:17:32 +01002165 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0f003;
2166 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x41001;
Angel Pons88521882020-01-05 20:21:20 +01002167 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002168 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x3e0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002169
Angel Pons7c49cb82020-03-16 23:17:32 +01002170 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002171 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002172
Angel Pons88521882020-01-05 20:21:20 +01002173 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002174 }
2175
Angel Pons7c49cb82020-03-16 23:17:32 +01002176 /* JEDEC reset */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002177 dram_jedecreset(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01002178
2179 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002180 dram_mrscommands(ctrl);
2181
2182 toggle_io_reset();
2183}
2184
2185#define MIN_C320C_LEN 13
2186
2187static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2188{
2189 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2190 int slotrank;
2191 int c320c;
2192 int stat[NUM_SLOTRANKS][256];
2193 int delta = 0;
2194
2195 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2196
2197 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002198 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002199 }
2200
2201 ctrl->cmd_stretch[channel] = cmd_stretch;
2202
Angel Pons88521882020-01-05 20:21:20 +01002203 MCHBAR32(TC_RAP_ch(channel)) =
Angel Pons7c49cb82020-03-16 23:17:32 +01002204 (ctrl->tRRD << 0)
2205 | (ctrl->tRTP << 4)
2206 | (ctrl->tCKE << 8)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002207 | (ctrl->tWTR << 12)
2208 | (ctrl->tFAW << 16)
Angel Pons7c49cb82020-03-16 23:17:32 +01002209 | (ctrl->tWR << 24)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002210 | (ctrl->cmd_stretch[channel] << 30);
2211
2212 if (ctrl->cmd_stretch[channel] == 2)
2213 delta = 2;
2214 else if (ctrl->cmd_stretch[channel] == 0)
2215 delta = 4;
2216
2217 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002218 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002219 }
2220
2221 for (c320c = -127; c320c <= 127; c320c++) {
2222 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002223 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002224 }
2225 program_timings(ctrl, channel);
2226 reprogram_320c(ctrl);
2227 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002228 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002229 }
2230 }
2231 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002232 struct run rn = get_longest_zero_run(stat[slotrank], 255);
2233
Angel Pons88521882020-01-05 20:21:20 +01002234 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002235 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2236 channel, slotrank, rn.start, rn.middle, rn.end);
Angel Pons7c49cb82020-03-16 23:17:32 +01002237
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002238 if (rn.all || rn.length < MIN_C320C_LEN) {
2239 FOR_ALL_POPULATED_RANKS {
2240 ctrl->timings[channel][slotrank] =
2241 saved_timings[channel][slotrank];
2242 }
2243 return MAKE_ERR;
2244 }
2245 }
2246
2247 return 0;
2248}
2249
Angel Pons7c49cb82020-03-16 23:17:32 +01002250/*
2251 * Adjust CMD phase shift and try multiple command rates.
2252 * A command rate of 2T doubles the time needed for address and command decode.
2253 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002254int command_training(ramctr_timing *ctrl)
2255{
2256 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002257
2258 FOR_ALL_POPULATED_CHANNELS {
2259 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002260 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002261 }
2262
2263 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002264 int cmdrate, err;
2265
2266 /*
2267 * Dual DIMM per channel:
Angel Pons7c49cb82020-03-16 23:17:32 +01002268 * Issue:
2269 * While c320c discovery seems to succeed raminit will fail in write training.
2270 *
2271 * Workaround:
2272 * Skip 1T in dual DIMM mode, that's only supported by a few DIMMs.
2273 * Only try 1T mode for XMP DIMMs that request it in dual DIMM mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002274 *
2275 * Single DIMM per channel:
2276 * Try command rate 1T and 2T
2277 */
2278 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002279 if (ctrl->tCMD)
2280 /* XMP gives the CMD rate in clock ticks, not ns */
2281 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002282
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002283 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002284 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2285
2286 if (!err)
2287 break;
2288 }
2289
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002290 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002291 printk(BIOS_EMERG, "c320c discovery failed\n");
2292 return err;
2293 }
2294
Angel Pons891f2bc2020-01-10 01:27:28 +01002295 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002296 }
2297
2298 FOR_ALL_POPULATED_CHANNELS
2299 program_timings(ctrl, channel);
2300
2301 reprogram_320c(ctrl);
2302 return 0;
2303}
2304
Angel Pons891f2bc2020-01-10 01:27:28 +01002305static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002306{
2307 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002308 int stats[NUM_LANES][MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002309 int lane;
2310
2311 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2312 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002313 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
Angel Pons891f2bc2020-01-10 01:27:28 +01002314 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002315 }
2316 program_timings(ctrl, channel);
2317
2318 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002319 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2320 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002321 }
2322
Angel Pons88521882020-01-05 20:21:20 +01002323 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002324
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002325 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002326 write MR3 MPR enable
2327 in this mode only RD and RDA are allowed
2328 all reads return a predefined pattern */
2329 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2330 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002331 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002332 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002333
2334 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002335 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2336 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x40411f4;
Angel Pons88521882020-01-05 20:21:20 +01002337 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002338 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002339
2340 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002341 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2342 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002343 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002344 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002345
2346 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002347 MR3 disable MPR */
2348 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2349 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0xc01 | (ctrl->tMOD << 16);
Angel Pons891f2bc2020-01-10 01:27:28 +01002350 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002351 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002352
Angel Pons7c49cb82020-03-16 23:17:32 +01002353 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002354 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002355
Angel Pons88521882020-01-05 20:21:20 +01002356 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002357
2358 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002359 stats[lane][edge] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002360 }
2361 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002362
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002363 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002364 struct run rn = get_longest_zero_run(stats[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002365 edges[lane] = rn.middle;
Angel Pons7c49cb82020-03-16 23:17:32 +01002366
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002367 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002368 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n", channel,
2369 slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002370 return MAKE_ERR;
2371 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002372 printram("eval %d, %d, %d: %02x\n", channel, slotrank, lane, edges[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002373 }
2374 return 0;
2375}
2376
2377int discover_edges(ramctr_timing *ctrl)
2378{
2379 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2380 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2381 int channel, slotrank, lane;
2382 int err;
2383
Angel Pons88521882020-01-05 20:21:20 +01002384 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002385
2386 toggle_io_reset();
2387
2388 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002389 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002390 }
2391
2392 FOR_ALL_POPULATED_CHANNELS {
2393 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002394 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002395 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002396 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002397 }
2398
2399 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002400 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2401 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002402 }
2403
2404 program_timings(ctrl, channel);
2405
2406 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002407 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002408
2409 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002410 MR3 enable MPR
2411 write MR3 MPR enable
2412 in this mode only RD and RDA are allowed
2413 all reads return a predefined pattern */
2414 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2415 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002416 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002417 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002418 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002419 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002420
2421 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002422 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2423 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons63ae8de2020-01-10 02:03:47 +01002424 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002425 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002426
2427 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002428 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2429 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002430 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002431 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002432 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002433 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002434
2435 /* DRAM command MRS
2436 * MR3 disable MPR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002437 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2438 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002439 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002440 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002441 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002442 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Felix Held9cf1dd22018-07-31 14:52:40 +02002443
Angel Pons7c49cb82020-03-16 23:17:32 +01002444 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002445 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002446
Angel Pons88521882020-01-05 20:21:20 +01002447 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002448 }
2449
2450 /* XXX: check any measured value ? */
2451
2452 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002453 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
Angel Pons7c49cb82020-03-16 23:17:32 +01002454 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002455 }
2456
2457 program_timings(ctrl, channel);
2458
2459 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002460 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002461
2462 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002463 MR3 enable MPR
2464 write MR3 MPR enable
2465 in this mode only RD and RDA are allowed
2466 all reads return a predefined pattern */
2467 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f000;
2468 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002469 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002470 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002471 (slotrank << 24) | 0x360004;
Angel Pons7c49cb82020-03-16 23:17:32 +01002472 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002473
2474 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002475 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f105;
2476 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x4041003;
Angel Pons88521882020-01-05 20:21:20 +01002477 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Angel Pons63ae8de2020-01-10 02:03:47 +01002478 (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002479 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002480
2481 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002482 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2483 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002484 0x1001 | ((ctrl->CAS + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002485 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002486 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002487 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002488
2489 /* DRAM command MRS
2490 * MR3 disable MPR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002491 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f000;
2492 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002493 0xc01 | (ctrl->tMOD << 16);
Angel Pons88521882020-01-05 20:21:20 +01002494 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002495 (slotrank << 24) | 0x360000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002496 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002497
Angel Pons7c49cb82020-03-16 23:17:32 +01002498 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002499 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002500
Angel Pons88521882020-01-05 20:21:20 +01002501 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002502 }
2503
2504 /* XXX: check any measured value ? */
2505
2506 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002507 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002508 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002509 }
2510
2511 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002512 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002513 }
2514
2515 /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */
Angel Pons88521882020-01-05 20:21:20 +01002516 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2517 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002518
2519 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2520 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002521 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002522 if (err)
2523 return err;
2524 }
2525
Angel Pons88521882020-01-05 20:21:20 +01002526 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2527 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002528
2529 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2530 err = discover_edges_real(ctrl, channel, slotrank,
2531 rising_edges[channel][slotrank]);
2532 if (err)
2533 return err;
2534 }
2535
Angel Pons88521882020-01-05 20:21:20 +01002536 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002537
2538 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2539 ctrl->timings[channel][slotrank].lanes[lane].falling =
2540 falling_edges[channel][slotrank][lane];
2541 ctrl->timings[channel][slotrank].lanes[lane].rising =
2542 rising_edges[channel][slotrank][lane];
2543 }
2544
2545 FOR_ALL_POPULATED_CHANNELS {
2546 program_timings(ctrl, channel);
2547 }
2548
2549 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002550 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002551 }
2552 return 0;
2553}
2554
Angel Pons7c49cb82020-03-16 23:17:32 +01002555static int discover_edges_write_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002556{
2557 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002558 u32 raw_stats[MAX_EDGE_TIMING + 1];
2559 int stats[MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002560 const int reg3000b24[] = { 0, 0xc, 0x2c };
2561 int lane, i;
2562 int lower[NUM_LANES];
2563 int upper[NUM_LANES];
2564 int pat;
2565
2566 FOR_ALL_LANES {
2567 lower[lane] = 0;
2568 upper[lane] = MAX_EDGE_TIMING;
2569 }
2570
2571 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002572 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002573 printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
2574
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002575 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2576 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002577 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002578 printram("using pattern %d\n", pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002579
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002580 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2581 FOR_ALL_LANES {
2582 ctrl->timings[channel][slotrank].lanes[lane].
2583 rising = edge;
2584 ctrl->timings[channel][slotrank].lanes[lane].
2585 falling = edge;
2586 }
2587 program_timings(ctrl, channel);
2588
2589 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002590 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2591 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002592 }
Angel Pons88521882020-01-05 20:21:20 +01002593 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002594
2595 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002596 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x1f006;
2597 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002598 0x4 | (ctrl->tRCD << 16) |
Angel Pons891f2bc2020-01-10 01:27:28 +01002599 (MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10);
Angel Pons88521882020-01-05 20:21:20 +01002600 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002601 (slotrank << 24) | 0x60000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002602 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x240;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002603
2604 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002605 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2606 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x8005020 |
Felix Held2bb3cdf2018-07-28 00:23:59 +02002607 ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002608 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002609 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002610 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002611
2612 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002613 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2614 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002615 0x4005020 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002616 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002617 slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002618 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002619
2620 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002621 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2622 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002623 0xc01 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002624 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002625 (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002626 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002627
Angel Pons7c49cb82020-03-16 23:17:32 +01002628 /* Execute command queue */
2629 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002630
Angel Pons88521882020-01-05 20:21:20 +01002631 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002632 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002633 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002634 }
2635
Angel Pons7c49cb82020-03-16 23:17:32 +01002636 /* FIXME: This register only exists on Ivy Bridge */
2637 raw_stats[edge] = MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002638 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002639
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002640 FOR_ALL_LANES {
2641 struct run rn;
2642 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
Angel Pons7c49cb82020-03-16 23:17:32 +01002643 stats[edge] = !!(raw_stats[edge] & (1 << lane));
2644
2645 rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
2646
2647 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, "
2648 "0x%02x-0x%02x\n", channel, slotrank, i, rn.start,
2649 rn.middle, rn.end, rn.start + ctrl->edge_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002650 rn.end - ctrl->edge_offset[i]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002651
2652 lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
2653 upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
2654
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002655 edges[lane] = (lower[lane] + upper[lane]) / 2;
2656 if (rn.all || (lower[lane] > upper[lane])) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002657 printk(BIOS_EMERG, "edge write discovery failed: "
2658 "%d, %d, %d\n", channel, slotrank, lane);
2659
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002660 return MAKE_ERR;
2661 }
2662 }
2663 }
2664 }
2665
Angel Pons88521882020-01-05 20:21:20 +01002666 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002667 printram("CPA\n");
2668 return 0;
2669}
2670
2671int discover_edges_write(ramctr_timing *ctrl)
2672{
2673 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
Angel Pons7c49cb82020-03-16 23:17:32 +01002674 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2675 int channel, slotrank, lane, err;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002676
Angel Pons7c49cb82020-03-16 23:17:32 +01002677 /*
2678 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2679 * also use a single loop. It would seem that it is a debugging configuration.
2680 */
Angel Pons88521882020-01-05 20:21:20 +01002681 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2682 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002683
2684 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2685 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002686 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002687 if (err)
2688 return err;
2689 }
2690
Angel Pons88521882020-01-05 20:21:20 +01002691 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2692 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002693
2694 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2695 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002696 rising_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002697 if (err)
2698 return err;
2699 }
2700
Angel Pons88521882020-01-05 20:21:20 +01002701 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002702
2703 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2704 ctrl->timings[channel][slotrank].lanes[lane].falling =
Angel Pons7c49cb82020-03-16 23:17:32 +01002705 falling_edges[channel][slotrank][lane];
2706
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002707 ctrl->timings[channel][slotrank].lanes[lane].rising =
Angel Pons7c49cb82020-03-16 23:17:32 +01002708 rising_edges[channel][slotrank][lane];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002709 }
2710
2711 FOR_ALL_POPULATED_CHANNELS
2712 program_timings(ctrl, channel);
2713
2714 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002715 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002716 }
2717 return 0;
2718}
2719
2720static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2721{
Angel Pons88521882020-01-05 20:21:20 +01002722 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002723
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002724 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002725 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0001f006;
2726 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002727 (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) | (ctrl->tRCD << 16) | 4;
Angel Pons7c49cb82020-03-16 23:17:32 +01002728 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = (slotrank << 24) | 0x60000;
2729 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x0244;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002730
2731 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002732 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x1f201;
2733 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002734 0x80011e0 | ((ctrl->tWTR + ctrl->CWL + 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002735 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002736 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002737
2738 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002739 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x1f105;
2740 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x40011e0 | (MAX(ctrl->tRTP, 8) << 16);
Angel Pons88521882020-01-05 20:21:20 +01002741 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002742 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x242;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002743
2744 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002745 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x1f002;
2746 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x1001 | (ctrl->tRP << 16);
Angel Pons88521882020-01-05 20:21:20 +01002747 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = (slotrank << 24) | 0x60400;
Angel Pons7c49cb82020-03-16 23:17:32 +01002748 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002749
Angel Pons7c49cb82020-03-16 23:17:32 +01002750 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002751 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002752
Angel Pons88521882020-01-05 20:21:20 +01002753 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002754}
2755
2756int discover_timC_write(ramctr_timing *ctrl)
2757{
Angel Pons7c49cb82020-03-16 23:17:32 +01002758 const u8 rege3c_b24[3] = { 0, 0x0f, 0x2f };
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002759 int i, pat;
2760
2761 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2762 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2763 int channel, slotrank, lane;
2764
2765 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2766 lower[channel][slotrank][lane] = 0;
2767 upper[channel][slotrank][lane] = MAX_TIMC;
2768 }
2769
Angel Pons88521882020-01-05 20:21:20 +01002770 /*
2771 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2772 * FIXME: This must only be done on Ivy Bridge.
2773 */
2774 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002775 printram("discover timC write:\n");
2776
2777 for (i = 0; i < 3; i++)
2778 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002779
2780 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
2781 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel),
2782 ~0x3f000000, rege3c_b24[i] << 24);
2783
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002784 udelay(2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002785
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002786 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2787 FOR_ALL_POPULATED_RANKS {
2788 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01002789 u32 raw_stats[MAX_TIMC + 1];
2790 int stats[MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002791
2792 /* Make sure rn.start < rn.end */
Angel Pons7c49cb82020-03-16 23:17:32 +01002793 stats[MAX_TIMC] = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002794
2795 fill_pattern5(ctrl, channel, pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002796 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
2797
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002798 for (timC = 0; timC < MAX_TIMC; timC++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002799 FOR_ALL_LANES {
2800 ctrl->timings[channel][slotrank]
2801 .lanes[lane].timC = timC;
2802 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002803 program_timings(ctrl, channel);
2804
2805 test_timC_write (ctrl, channel, slotrank);
2806
Angel Pons7c49cb82020-03-16 23:17:32 +01002807 /* FIXME: Another IVB-only register! */
2808 raw_stats[timC] =
Angel Pons1aba2a32020-01-05 22:31:41 +01002809 MCHBAR32(0x436c + channel * 0x400);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002810 }
2811 FOR_ALL_LANES {
2812 struct run rn;
Angel Pons7c49cb82020-03-16 23:17:32 +01002813 for (timC = 0; timC < MAX_TIMC; timC++) {
2814 stats[timC] = !!(raw_stats[timC]
2815 & (1 << lane));
2816 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002817
Angel Pons7c49cb82020-03-16 23:17:32 +01002818 rn = get_longest_zero_run(stats, MAX_TIMC + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002819 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002820 printk(BIOS_EMERG,
2821 "timC write discovery failed: "
2822 "%d, %d, %d\n", channel,
2823 slotrank, lane);
2824
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002825 return MAKE_ERR;
2826 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002827 printram("timC: %d, %d, %d: "
2828 "0x%02x-0x%02x-0x%02x, "
2829 "0x%02x-0x%02x\n", channel, slotrank,
2830 i, rn.start, rn.middle, rn.end,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002831 rn.start + ctrl->timC_offset[i],
Angel Pons7c49cb82020-03-16 23:17:32 +01002832 rn.end - ctrl->timC_offset[i]);
2833
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002834 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002835 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002836 lower[channel][slotrank][lane]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002837
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002838 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002839 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002840 upper[channel][slotrank][lane]);
2841
2842 }
2843 }
2844 }
2845 }
2846
2847 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002848 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
Angel Pons88521882020-01-05 20:21:20 +01002849 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002850 udelay(2);
2851 }
2852
Angel Pons88521882020-01-05 20:21:20 +01002853 /*
2854 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2855 * FIXME: This must only be done on Ivy Bridge.
2856 */
2857 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002858
2859 printram("CPB\n");
2860
2861 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002862 printram("timC %d, %d, %d: %x\n", channel, slotrank, lane,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002863 (lower[channel][slotrank][lane] +
2864 upper[channel][slotrank][lane]) / 2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002865
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002866 ctrl->timings[channel][slotrank].lanes[lane].timC =
2867 (lower[channel][slotrank][lane] +
2868 upper[channel][slotrank][lane]) / 2;
2869 }
2870 FOR_ALL_POPULATED_CHANNELS {
2871 program_timings(ctrl, channel);
2872 }
2873 return 0;
2874}
2875
Angel Pons88521882020-01-05 20:21:20 +01002876void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002877{
2878 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002879 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002880
2881 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2882 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002883 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002884 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002885 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01002886 printram("normalize %d, %d, %d: mat %d\n",
2887 channel, slotrank, lane, mat);
2888
Felix Heldef4fe3e2019-12-31 14:15:05 +01002889 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01002890 printram("normalize %d, %d, %d: delta %d\n",
2891 channel, slotrank, lane, delta);
2892
Angel Pons88521882020-01-05 20:21:20 +01002893 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01002894 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002895 }
2896
2897 FOR_ALL_POPULATED_CHANNELS {
2898 program_timings(ctrl, channel);
2899 }
2900}
2901
Angel Pons88521882020-01-05 20:21:20 +01002902void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002903{
2904 int channel, slotrank;
2905
2906 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002907 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002908 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01002909 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002910 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002911 }
2912}
2913
2914int channel_test(ramctr_timing *ctrl)
2915{
2916 int channel, slotrank, lane;
2917
2918 slotrank = 0;
2919 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002920 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01002921 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002922 return MAKE_ERR;
2923 }
2924 FOR_ALL_POPULATED_CHANNELS {
2925 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
2926
Angel Pons88521882020-01-05 20:21:20 +01002927 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002928 }
2929
2930 for (slotrank = 0; slotrank < 4; slotrank++)
2931 FOR_ALL_CHANNELS
2932 if (ctrl->rankmap[channel] & (1 << slotrank)) {
2933 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002934 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
2935 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002936 }
Angel Pons88521882020-01-05 20:21:20 +01002937 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002938
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002939 /* DRAM command ACT */
Angel Pons7c49cb82020-03-16 23:17:32 +01002940 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = 0x0001f006;
2941 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = 0x0028a004;
Angel Pons891f2bc2020-01-10 01:27:28 +01002942 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = 0x00060000 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002943 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x00000244;
Felix Held9cf1dd22018-07-31 14:52:40 +02002944
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002945 /* DRAM command WR */
Angel Pons7c49cb82020-03-16 23:17:32 +01002946 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = 0x0001f201;
2947 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x08281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002948 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002949 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002950
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002951 /* DRAM command RD */
Angel Pons7c49cb82020-03-16 23:17:32 +01002952 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = 0x0001f105;
2953 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x04281064;
Angel Pons63ae8de2020-01-10 02:03:47 +01002954 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = slotrank << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002955 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x00000242;
Felix Held9cf1dd22018-07-31 14:52:40 +02002956
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002957 /* DRAM command PRE */
Angel Pons7c49cb82020-03-16 23:17:32 +01002958 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 3)) = 0x0001f002;
2959 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 3)) = 0x00280c01;
Angel Pons891f2bc2020-01-10 01:27:28 +01002960 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 3)) = 0x00060400 | (slotrank << 24);
Angel Pons7c49cb82020-03-16 23:17:32 +01002961 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 3)) = 0x00000240;
Felix Held9cf1dd22018-07-31 14:52:40 +02002962
Angel Pons7c49cb82020-03-16 23:17:32 +01002963 /* Execute command queue */
Angel Pons88521882020-01-05 20:21:20 +01002964 MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002965
Angel Pons88521882020-01-05 20:21:20 +01002966 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002967 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01002968 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002969 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
2970 channel, slotrank, lane);
2971 return MAKE_ERR;
2972 }
2973 }
2974 return 0;
2975}
2976
Angel Pons88521882020-01-05 20:21:20 +01002977void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002978{
2979 int channel;
2980
Angel Pons7c49cb82020-03-16 23:17:32 +01002981 /* 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 +01002982 static u32 seeds[NUM_CHANNELS][3] = {
2983 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
2984 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
2985 };
2986 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002987 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
Angel Pons7c49cb82020-03-16 23:17:32 +01002988 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
2989 MCHBAR32(SCRAMBLING_SEED_2_HI_ch(channel)) = seeds[channel][1];
2990 MCHBAR32(SCRAMBLING_SEED_2_LO_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002991 }
2992}
2993
Angel Pons89ae6b82020-03-21 13:23:32 +01002994void set_wmm_behavior(const u32 cpu)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002995{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002996 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002997 MCHBAR32(SC_WDBWM) = 0x141d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002998 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002999 MCHBAR32(SC_WDBWM) = 0x551d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003000 }
3001}
3002
Angel Pons88521882020-01-05 20:21:20 +01003003void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003004{
3005 int channel;
3006
3007 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003008 /* Always drive command bus */
Angel Pons88521882020-01-05 20:21:20 +01003009 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003010 }
3011
3012 udelay(1);
3013
3014 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003015 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003016 }
3017}
3018
Angel Pons7c49cb82020-03-16 23:17:32 +01003019void set_read_write_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003020{
3021 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003022
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003023 FOR_ALL_POPULATED_CHANNELS {
3024 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003025 int min_pi = 10000;
3026 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003027
3028 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003029 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3030 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003031 }
3032
Angel Pons7c49cb82020-03-16 23:17:32 +01003033 b20 = (max_pi - min_pi > 51) ? 0 : ctrl->ref_card_offset[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003034
Angel Pons7c49cb82020-03-16 23:17:32 +01003035 b4_8_12 = (ctrl->pi_coding_threshold < max_pi - min_pi) ? 0x3330 : 0x2220;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003036
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003037 dram_odt_stretch(ctrl, channel);
3038
Angel Pons7c49cb82020-03-16 23:17:32 +01003039 MCHBAR32(TC_RWP_ch(channel)) = 0x0a000000 | (b20 << 20) |
Felix Held2463aa92018-07-29 21:37:55 +02003040 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003041 }
3042}
3043
Angel Pons88521882020-01-05 20:21:20 +01003044void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003045{
3046 int channel;
3047 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003048 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3049 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003050 }
3051}
3052
Angel Pons7c49cb82020-03-16 23:17:32 +01003053/* Encode the watermark latencies in a suitable format for graphics drivers consumption */
3054static int encode_wm(int ns)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003055{
Angel Pons88521882020-01-05 20:21:20 +01003056 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003057}
3058
Angel Pons7c49cb82020-03-16 23:17:32 +01003059/* FIXME: values in this function should be hardware revision-dependent */
Angel Pons88521882020-01-05 20:21:20 +01003060void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003061{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003062 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3063
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003064 int channel;
3065 int t1_cycles = 0, t1_ns = 0, t2_ns;
3066 int t3_ns;
3067 u32 r32;
3068
Angel Pons7c49cb82020-03-16 23:17:32 +01003069 /* FIXME: This register only exists on Ivy Bridge */
3070 MCHBAR32(WMM_READ_CONFIG) = 0x46;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003071
Felix Heldf9b826a2018-07-30 17:56:52 +02003072 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003073 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xffffcfff, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003074
Patrick Rudolph74203de2017-11-20 11:57:01 +01003075 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003076 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003077 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003078 else
Angel Pons7c49cb82020-03-16 23:17:32 +01003079 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003080 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003081
Felix Heldf9b826a2018-07-30 17:56:52 +02003082 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003083 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003084
Angel Pons88521882020-01-05 20:21:20 +01003085 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3086 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003087
3088 FOR_ALL_CHANNELS {
3089 switch (ctrl->rankmap[channel]) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003090 /* Unpopulated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003091 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003092 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003093 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003094 /* Only single-ranked dimms */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003095 case 1:
3096 case 4:
3097 case 5:
Angel Pons7c49cb82020-03-16 23:17:32 +01003098 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x00373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003099 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003100 /* Dual-ranked dimms present */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003101 default:
Angel Pons7c49cb82020-03-16 23:17:32 +01003102 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x009b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003103 break;
3104 }
3105 }
3106
Felix Held50b7ed22019-12-30 20:41:54 +01003107 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
Angel Pons7c49cb82020-03-16 23:17:32 +01003108 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0x00ffffff, 0x00e4d5d0);
Felix Held50b7ed22019-12-30 20:41:54 +01003109 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003110
3111 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003112 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~(3 << 16), 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003113
Angel Pons88521882020-01-05 20:21:20 +01003114 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3115 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3116 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003117
Angel Pons7c49cb82020-03-16 23:17:32 +01003118 /* Find a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003119 FOR_ALL_POPULATED_CHANNELS
3120 break;
3121
Angel Pons88521882020-01-05 20:21:20 +01003122 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3123 r32 = MCHBAR32(PM_DLL_CONFIG);
Angel Pons7c49cb82020-03-16 23:17:32 +01003124 if (r32 & (1 << 17))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003125 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003126 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003127 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
Angel Pons7c49cb82020-03-16 23:17:32 +01003128 if (!(r32 & (1 << 17)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003129 t1_ns += 500;
3130
Angel Pons88521882020-01-05 20:21:20 +01003131 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003132 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003133 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003134 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003135 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003136 t3_ns = 500;
3137 }
Angel Pons7c49cb82020-03-16 23:17:32 +01003138
3139 /* The graphics driver will use these watermark values */
3140 printk(BIOS_DEBUG, "t123: %d, %d, %d\n", t1_ns, t2_ns, t3_ns);
3141 MCHBAR32_AND_OR(SSKPD, 0xC0C0C0C0,
3142 ((encode_wm(t1_ns) + encode_wm(t2_ns)) << 16) | (encode_wm(t1_ns) << 8) |
3143 ((encode_wm(t3_ns) + encode_wm(t2_ns) + encode_wm(t1_ns)) << 24) | 0x0c);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003144}
3145
Angel Pons88521882020-01-05 20:21:20 +01003146void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003147{
3148 int channel, slotrank, lane;
3149
Angel Pons7c49cb82020-03-16 23:17:32 +01003150 FOR_ALL_POPULATED_CHANNELS {
3151 MCHBAR32(TC_RAP_ch(channel)) =
3152 (ctrl->tRRD << 0)
3153 | (ctrl->tRTP << 4)
3154 | (ctrl->tCKE << 8)
3155 | (ctrl->tWTR << 12)
3156 | (ctrl->tFAW << 16)
3157 | (ctrl->tWR << 24)
3158 | (ctrl->cmd_stretch[channel] << 30);
3159 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003160
3161 udelay(1);
3162
3163 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003164 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003165 }
3166
3167 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003168 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003169 }
3170
3171 FOR_ALL_POPULATED_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003172 MCHBAR32_OR(TC_RWP_ch(channel), 0x08000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003173
3174 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003175 udelay(1);
3176 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x00200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003177 }
3178
3179 printram("CPE\n");
3180
Angel Pons88521882020-01-05 20:21:20 +01003181 MCHBAR32(GDCRTRAININGMOD) = 0;
3182 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003183
3184 printram("CP5b\n");
3185
3186 FOR_ALL_POPULATED_CHANNELS {
3187 program_timings(ctrl, channel);
3188 }
3189
3190 u32 reg, addr;
3191
Angel Pons7c49cb82020-03-16 23:17:32 +01003192 /* Poll for RCOMP */
3193 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
3194 ;
3195
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003196 do {
Angel Pons88521882020-01-05 20:21:20 +01003197 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003198 } while ((reg & 0x14) == 0);
3199
Angel Pons7c49cb82020-03-16 23:17:32 +01003200 /* Set state of memory controller */
Angel Pons88521882020-01-05 20:21:20 +01003201 MCHBAR32(MC_INIT_STATE_G) = 0x116;
Angel Pons7c49cb82020-03-16 23:17:32 +01003202 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003203
Angel Pons7c49cb82020-03-16 23:17:32 +01003204 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003205 udelay(500);
3206
3207 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003208 /* Set valid rank CKE */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003209 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01003210 reg = (reg & ~0x0f) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003211 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003212 MCHBAR32(addr) = reg;
3213
Angel Pons7c49cb82020-03-16 23:17:32 +01003214 /* Wait 10ns for ranks to settle */
3215 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003216
3217 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3218 MCHBAR32(addr) = reg;
3219
Angel Pons7c49cb82020-03-16 23:17:32 +01003220 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003221 write_reset(ctrl);
3222 }
3223
Angel Pons7c49cb82020-03-16 23:17:32 +01003224 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003225 dram_mrscommands(ctrl);
3226
3227 printram("CP5c\n");
3228
Angel Pons88521882020-01-05 20:21:20 +01003229 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003230
3231 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003232 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003233 udelay(2);
3234 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003235}