blob: c4bf5ff50eb1b006ea37a014d7d7071b4747bf64 [file] [log] [blame]
Angel Pons6e5aabd2020-03-23 23:44:42 +01001/* SPDX-License-Identifier: GPL-2.0-only */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01003#include <commonlib/helpers.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01004#include <console/console.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01005#include <string.h>
Subrata Banik53b08c32018-12-10 14:11:35 +05306#include <arch/cpu.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +02007#include <device/mmio.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +02008#include <device/pci_ops.h>
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01009#include <northbridge/intel/sandybridge/chip.h>
10#include <device/pci_def.h>
11#include <delay.h>
Elyes HAOUAS1d3b3c32019-05-04 08:12:42 +020012
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010013#include "raminit_native.h"
14#include "raminit_common.h"
Angel Pons7f6586f2020-03-21 12:45:12 +010015#include "raminit_tables.h"
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010016#include "sandybridge.h"
17
Angel Pons7c49cb82020-03-16 23:17:32 +010018/* FIXME: no support for 3-channel chipsets */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010019
Angel Ponsd5b780c2020-05-02 21:48:46 +020020/* Number of programmed IOSAV subsequences. */
21static unsigned int ssq_count = 0;
22
23static void iosav_write_ssq(const int ch, const int n, const struct iosav_ssq *ssq)
24{
25 MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(ch, ssq_count)) = ssq->sp_cmd_ctrl.raw;
26 MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(ch, ssq_count)) = ssq->subseq_ctrl.raw;
27 MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(ch, ssq_count)) = ssq->sp_cmd_addr.raw;
28 MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(ch, ssq_count)) = ssq->addr_update.raw;
29
30 ssq_count++;
31}
32
Angel Pons88521882020-01-05 20:21:20 +010033/* length: [1..4] */
Angel Ponse7afcd532020-05-02 23:14:27 +020034static void iosav_run_queue(const int ch, const u8 loops, const u8 length, const u8 as_timer)
35{
Angel Ponsd5b780c2020-05-02 21:48:46 +020036 MCHBAR32(IOSAV_SEQ_CTL_ch(ch)) = loops | ((ssq_count - 1) << 18) | (as_timer << 22);
37
38 ssq_count = 0;
Angel Ponse7afcd532020-05-02 23:14:27 +020039}
Angel Ponsad704002020-05-02 22:51:58 +020040
Angel Ponse7afcd532020-05-02 23:14:27 +020041static void iosav_run_once(const int ch, const u8 length)
42{
43 iosav_run_queue(ch, 1, length, 0);
44}
Felix Held9cf1dd22018-07-31 14:52:40 +020045
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010046static void sfence(void)
47{
48 asm volatile ("sfence");
49}
50
Angel Pons7c49cb82020-03-16 23:17:32 +010051/* Toggle IO reset bit */
52static void toggle_io_reset(void)
53{
Angel Pons88521882020-01-05 20:21:20 +010054 u32 r32 = MCHBAR32(MC_INIT_STATE_G);
Angel Pons7c49cb82020-03-16 23:17:32 +010055 MCHBAR32(MC_INIT_STATE_G) = r32 | 0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010056 udelay(1);
Angel Pons88521882020-01-05 20:21:20 +010057 MCHBAR32(MC_INIT_STATE_G) = r32 & ~0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010058 udelay(1);
59}
60
61static u32 get_XOVER_CLK(u8 rankmap)
62{
63 return rankmap << 24;
64}
65
66static u32 get_XOVER_CMD(u8 rankmap)
67{
68 u32 reg;
69
Angel Pons7c49cb82020-03-16 23:17:32 +010070 /* Enable xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010071 reg = 0x4000;
72
Angel Pons7c49cb82020-03-16 23:17:32 +010073 /* Enable xover ctl */
74 if (rankmap & 0x03)
75 reg |= (1 << 17);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010076
Angel Pons7c49cb82020-03-16 23:17:32 +010077 if (rankmap & 0x0c)
78 reg |= (1 << 26);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010079
80 return reg;
81}
82
Angel Pons7c49cb82020-03-16 23:17:32 +010083/* CAS write latency. To be programmed in MR2. See DDR3 SPEC for MR2 documentation. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010084u8 get_CWL(u32 tCK)
85{
Angel Pons7c49cb82020-03-16 23:17:32 +010086 /* Get CWL based on tCK using the following rule */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010087 switch (tCK) {
88 case TCK_1333MHZ:
89 return 12;
Angel Pons7c49cb82020-03-16 23:17:32 +010090
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010091 case TCK_1200MHZ:
92 case TCK_1100MHZ:
93 return 11;
Angel Pons7c49cb82020-03-16 23:17:32 +010094
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010095 case TCK_1066MHZ:
96 case TCK_1000MHZ:
97 return 10;
Angel Pons7c49cb82020-03-16 23:17:32 +010098
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010099 case TCK_933MHZ:
100 case TCK_900MHZ:
101 return 9;
Angel Pons7c49cb82020-03-16 23:17:32 +0100102
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100103 case TCK_800MHZ:
104 case TCK_700MHZ:
105 return 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100106
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100107 case TCK_666MHZ:
108 return 7;
Angel Pons7c49cb82020-03-16 23:17:32 +0100109
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100110 case TCK_533MHZ:
111 return 6;
Angel Pons7c49cb82020-03-16 23:17:32 +0100112
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100113 default:
114 return 5;
115 }
116}
117
118void dram_find_common_params(ramctr_timing *ctrl)
119{
120 size_t valid_dimms;
121 int channel, slot;
122 dimm_info *dimms = &ctrl->info;
123
124 ctrl->cas_supported = (1 << (MAX_CAS - MIN_CAS + 1)) - 1;
125 valid_dimms = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100126
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100127 FOR_ALL_CHANNELS for (slot = 0; slot < 2; slot++) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100128
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100129 const dimm_attr *dimm = &dimms->dimm[channel][slot];
130 if (dimm->dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3)
131 continue;
Angel Pons7c49cb82020-03-16 23:17:32 +0100132
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100133 valid_dimms++;
134
135 /* Find all possible CAS combinations */
136 ctrl->cas_supported &= dimm->cas_supported;
137
138 /* Find the smallest common latencies supported by all DIMMs */
Angel Pons7c49cb82020-03-16 23:17:32 +0100139 ctrl->tCK = MAX(ctrl->tCK, dimm->tCK);
140 ctrl->tAA = MAX(ctrl->tAA, dimm->tAA);
141 ctrl->tWR = MAX(ctrl->tWR, dimm->tWR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100142 ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD);
143 ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD);
Angel Pons7c49cb82020-03-16 23:17:32 +0100144 ctrl->tRP = MAX(ctrl->tRP, dimm->tRP);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100145 ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS);
146 ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC);
147 ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR);
148 ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP);
149 ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW);
Dan Elkoubydabebc32018-04-13 18:47:10 +0300150 ctrl->tCWL = MAX(ctrl->tCWL, dimm->tCWL);
151 ctrl->tCMD = MAX(ctrl->tCMD, dimm->tCMD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100152 }
153
154 if (!ctrl->cas_supported)
Angel Pons7c49cb82020-03-16 23:17:32 +0100155 die("Unsupported DIMM combination. DIMMS do not support common CAS latency");
156
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100157 if (!valid_dimms)
158 die("No valid DIMMs found");
159}
160
Angel Pons88521882020-01-05 20:21:20 +0100161void dram_xover(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100162{
163 u32 reg;
164 int channel;
165
166 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100167 /* Enable xover clk */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100168 reg = get_XOVER_CLK(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100169 printram("XOVER CLK [%x] = %x\n", GDCRCKPICODE_ch(channel), reg);
170 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100171
Angel Pons7c49cb82020-03-16 23:17:32 +0100172 /* Enable xover ctl & xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100173 reg = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100174 printram("XOVER CMD [%x] = %x\n", GDCRCMDPICODING_ch(channel), reg);
175 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100176 }
177}
178
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100179static void dram_odt_stretch(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100180{
Angel Pons89ae6b82020-03-21 13:23:32 +0100181 u32 addr, stretch;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100182
183 stretch = ctrl->ref_card_offset[channel];
Angel Pons7c49cb82020-03-16 23:17:32 +0100184 /*
185 * ODT stretch:
186 * Delay ODT signal by stretch value. Useful for multi DIMM setups on the same channel.
187 */
Angel Pons89ae6b82020-03-21 13:23:32 +0100188 if (IS_SANDY_CPU(ctrl->cpu) && IS_SANDY_CPU_C(ctrl->cpu)) {
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100189 if (stretch == 2)
190 stretch = 3;
Angel Pons7c49cb82020-03-16 23:17:32 +0100191
Angel Pons88521882020-01-05 20:21:20 +0100192 addr = SCHED_SECOND_CBIT_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100193 MCHBAR32_AND_OR(addr, 0xffffc3ff, (stretch << 12) | (stretch << 10));
194 printk(RAM_DEBUG, "OTHP Workaround [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100195 } else {
Angel Pons88521882020-01-05 20:21:20 +0100196 addr = TC_OTHP_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100197 MCHBAR32_AND_OR(addr, 0xfff0ffff, (stretch << 16) | (stretch << 18));
Iru Cai89af71c2018-08-16 16:46:27 +0800198 printk(RAM_DEBUG, "OTHP [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100199 }
200}
201
202void dram_timing_regs(ramctr_timing *ctrl)
203{
204 u32 reg, addr, val32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100205 int channel;
206
207 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100208 /* BIN parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100209 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100210 reg |= (ctrl->tRCD << 0);
211 reg |= (ctrl->tRP << 4);
212 reg |= (ctrl->CAS << 8);
213 reg |= (ctrl->CWL << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100214 reg |= (ctrl->tRAS << 16);
Angel Pons88521882020-01-05 20:21:20 +0100215 printram("DBP [%x] = %x\n", TC_DBP_ch(channel), reg);
216 MCHBAR32(TC_DBP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100217
Angel Pons7c49cb82020-03-16 23:17:32 +0100218 /* Regular access parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100219 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100220 reg |= (ctrl->tRRD << 0);
221 reg |= (ctrl->tRTP << 4);
222 reg |= (ctrl->tCKE << 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100223 reg |= (ctrl->tWTR << 12);
224 reg |= (ctrl->tFAW << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +0100225 reg |= (ctrl->tWR << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100226 reg |= (3 << 30);
Angel Pons88521882020-01-05 20:21:20 +0100227 printram("RAP [%x] = %x\n", TC_RAP_ch(channel), reg);
228 MCHBAR32(TC_RAP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100229
Angel Pons7c49cb82020-03-16 23:17:32 +0100230 /* Other parameters */
Angel Pons88521882020-01-05 20:21:20 +0100231 addr = TC_OTHP_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100232 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100233 reg |= (ctrl->tXPDLL << 0);
234 reg |= (ctrl->tXP << 5);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100235 reg |= (ctrl->tAONPD << 8);
236 reg |= 0xa0000;
237 printram("OTHP [%x] = %x\n", addr, reg);
238 MCHBAR32(addr) = reg;
239
Angel Ponsca2f68a2020-03-22 13:15:12 +0100240 /* Debug parameters - only applies to Ivy Bridge */
241 if (IS_IVY_CPU(ctrl->cpu)) {
242 reg = 0;
243
244 /*
245 * If tXP and tXPDLL are very high, we need to increase them by one.
246 * This can only happen on Ivy Bridge, and when overclocking the RAM.
247 */
248 if (ctrl->tXP >= 8)
249 reg |= (1 << 12);
250
251 if (ctrl->tXPDLL >= 32)
252 reg |= (1 << 13);
253
254 MCHBAR32(TC_DTP_ch(channel)) = reg;
255 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100256
Felix Held9fe248f2018-07-31 20:59:45 +0200257 MCHBAR32_OR(addr, 0x00020000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100258
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100259 dram_odt_stretch(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100260
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100261 /*
Angel Pons7c49cb82020-03-16 23:17:32 +0100262 * TC-Refresh timing parameters:
263 * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow
264 * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024.
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100265 */
266 val32 = MIN((ctrl->tREFI * 89) / 10, (70000 << 8) / ctrl->tCK);
267
Angel Pons7c49cb82020-03-16 23:17:32 +0100268 reg = ((ctrl->tREFI & 0xffff) << 0) |
269 ((ctrl->tRFC & 0x01ff) << 16) | (((val32 / 1024) & 0x7f) << 25);
270
Angel Pons88521882020-01-05 20:21:20 +0100271 printram("REFI [%x] = %x\n", TC_RFTP_ch(channel), reg);
272 MCHBAR32(TC_RFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100273
Angel Pons88521882020-01-05 20:21:20 +0100274 MCHBAR32_OR(TC_RFP_ch(channel), 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100275
Angel Pons7c49cb82020-03-16 23:17:32 +0100276 /* Self-refresh timing parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100277 reg = 0;
278 val32 = tDLLK;
Angel Pons7c49cb82020-03-16 23:17:32 +0100279 reg = (reg & ~0x00000fff) | (val32 << 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100280 val32 = ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100281 reg = (reg & ~0x0000f000) | (val32 << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100282 val32 = tDLLK - ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100283 reg = (reg & ~0x03ff0000) | (val32 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100284 val32 = ctrl->tMOD - 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100285 reg = (reg & ~0xf0000000) | (val32 << 28);
286 printram("SRFTP [%x] = %x\n", TC_SRFTP_ch(channel), reg);
Angel Pons88521882020-01-05 20:21:20 +0100287 MCHBAR32(TC_SRFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100288 }
289}
290
291void dram_dimm_mapping(ramctr_timing *ctrl)
292{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100293 int channel;
294 dimm_info *info = &ctrl->info;
295
296 FOR_ALL_CHANNELS {
Nico Huberac4f2162017-10-01 18:14:43 +0200297 dimm_attr *dimmA, *dimmB;
298 u32 reg = 0;
299
Angel Pons7c49cb82020-03-16 23:17:32 +0100300 if (info->dimm[channel][0].size_mb >= info->dimm[channel][1].size_mb) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100301 dimmA = &info->dimm[channel][0];
302 dimmB = &info->dimm[channel][1];
Angel Pons7c49cb82020-03-16 23:17:32 +0100303 reg |= (0 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100304 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100305 dimmA = &info->dimm[channel][1];
306 dimmB = &info->dimm[channel][0];
Angel Pons7c49cb82020-03-16 23:17:32 +0100307 reg |= (1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100308 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100309
Nico Huberac4f2162017-10-01 18:14:43 +0200310 if (dimmA && (dimmA->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100311 reg |= (dimmA->size_mb / 256) << 0;
312 reg |= (dimmA->ranks - 1) << 17;
Nico Huberac4f2162017-10-01 18:14:43 +0200313 reg |= (dimmA->width / 8 - 1) << 19;
314 }
315
316 if (dimmB && (dimmB->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100317 reg |= (dimmB->size_mb / 256) << 8;
318 reg |= (dimmB->ranks - 1) << 18;
Nico Huberac4f2162017-10-01 18:14:43 +0200319 reg |= (dimmB->width / 8 - 1) << 20;
320 }
321
Angel Pons7c49cb82020-03-16 23:17:32 +0100322 reg |= 1 << 21; /* Rank interleave */
323 reg |= 1 << 22; /* Enhanced interleave */
Nico Huberac4f2162017-10-01 18:14:43 +0200324
Angel Pons7c49cb82020-03-16 23:17:32 +0100325 if ((dimmA && (dimmA->ranks > 0)) || (dimmB && (dimmB->ranks > 0))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100326 ctrl->mad_dimm[channel] = reg;
327 } else {
328 ctrl->mad_dimm[channel] = 0;
329 }
330 }
331}
332
Patrick Rudolphdd662872017-10-28 18:20:11 +0200333void dram_dimm_set_mapping(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100334{
335 int channel;
Patrick Rudolphdd662872017-10-28 18:20:11 +0200336 u32 ecc;
337
338 if (ctrl->ecc_enabled)
339 ecc = training ? (1 << 24) : (3 << 24);
340 else
341 ecc = 0;
342
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100343 FOR_ALL_CHANNELS {
Patrick Rudolphdd662872017-10-28 18:20:11 +0200344 MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel] | ecc;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100345 }
Patrick Rudolphdd662872017-10-28 18:20:11 +0200346
347 //udelay(10); /* TODO: Might be needed for ECC configurations; so far works without. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100348}
349
Angel Pons88521882020-01-05 20:21:20 +0100350void dram_zones(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100351{
352 u32 reg, ch0size, ch1size;
353 u8 val;
354 reg = 0;
355 val = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100356
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100357 if (training) {
358 ch0size = ctrl->channel_size_mb[0] ? 256 : 0;
359 ch1size = ctrl->channel_size_mb[1] ? 256 : 0;
360 } else {
361 ch0size = ctrl->channel_size_mb[0];
362 ch1size = ctrl->channel_size_mb[1];
363 }
364
365 if (ch0size >= ch1size) {
Angel Pons88521882020-01-05 20:21:20 +0100366 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100367 val = ch1size / 256;
368 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100369 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100370 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100371 MCHBAR32(MAD_CHNL) = 0x24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100372
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100373 } else {
Angel Pons88521882020-01-05 20:21:20 +0100374 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100375 val = ch0size / 256;
376 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100377 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100378 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100379 MCHBAR32(MAD_CHNL) = 0x21;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100380 }
381}
382
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100383#define DEFAULT_PCI_MMIO_SIZE 2048
384
385static unsigned int get_mmio_size(void)
386{
387 const struct device *dev;
388 const struct northbridge_intel_sandybridge_config *cfg = NULL;
389
Angel Ponsb31d1d72020-01-10 01:35:09 +0100390 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100391 if (dev)
392 cfg = dev->chip_info;
393
394 /* If this is zero, it just means devicetree.cb didn't set it */
395 if (!cfg || cfg->pci_mmio_size == 0)
396 return DEFAULT_PCI_MMIO_SIZE;
397 else
398 return cfg->pci_mmio_size;
399}
400
Patrick Rudolph05d4bf7e2017-10-28 16:36:09 +0200401/*
402 * Returns the ECC mode the NB is running at. It takes precedence over ECC capability.
403 * The ME/PCU/.. has the ability to change this.
404 * Return 0: ECC is optional
405 * Return 1: ECC is forced
406 */
407bool get_host_ecc_forced(void)
408{
409 /* read Capabilities A Register */
410 const u32 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
411 return !!(reg32 & (1 << 24));
412}
413
414/*
415 * Returns the ECC capability.
416 * The ME/PCU/.. has the ability to change this.
417 * Return 0: ECC is disabled
418 * Return 1: ECC is possible
419 */
420bool get_host_ecc_cap(void)
421{
422 /* read Capabilities A Register */
423 const u32 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
424 return !(reg32 & (1 << 25));
425}
426
Angel Pons88521882020-01-05 20:21:20 +0100427void dram_memorymap(ramctr_timing *ctrl, int me_uma_size)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100428{
Angel Pons7c49cb82020-03-16 23:17:32 +0100429 u32 reg, val, reclaim, tom, gfxstolen, gttsize;
430 size_t tsegbase, toludbase, remapbase, gfxstolenbase, mmiosize, gttbase;
431 size_t tsegsize, touudbase, remaplimit, mestolenbase, tsegbasedelta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100432 uint16_t ggc;
433
434 mmiosize = get_mmio_size();
435
Felix Held87ddea22020-01-26 04:55:27 +0100436 ggc = pci_read_config16(HOST_BRIDGE, GGC);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100437 if (!(ggc & 2)) {
438 gfxstolen = ((ggc >> 3) & 0x1f) * 32;
Angel Pons7c49cb82020-03-16 23:17:32 +0100439 gttsize = ((ggc >> 8) & 0x3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100440 } else {
441 gfxstolen = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100442 gttsize = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100443 }
444
445 tsegsize = CONFIG_SMM_TSEG_SIZE >> 20;
446
447 tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1];
448
449 mestolenbase = tom - me_uma_size;
450
Angel Pons7c49cb82020-03-16 23:17:32 +0100451 toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize, tom - me_uma_size);
452
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100453 gfxstolenbase = toludbase - gfxstolen;
454 gttbase = gfxstolenbase - gttsize;
455
456 tsegbase = gttbase - tsegsize;
457
Angel Pons7c49cb82020-03-16 23:17:32 +0100458 /* Round tsegbase down to nearest address aligned to tsegsize */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100459 tsegbasedelta = tsegbase & (tsegsize - 1);
460 tsegbase &= ~(tsegsize - 1);
461
462 gttbase -= tsegbasedelta;
463 gfxstolenbase -= tsegbasedelta;
464 toludbase -= tsegbasedelta;
465
Angel Pons7c49cb82020-03-16 23:17:32 +0100466 /* Test if it is possible to reclaim a hole in the RAM addressing */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100467 if (tom - me_uma_size > toludbase) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100468 /* Reclaim is possible */
469 reclaim = 1;
470 remapbase = MAX(4096, tom - me_uma_size);
471 remaplimit = remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1;
472 touudbase = remaplimit + 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100473 } else {
474 // Reclaim not possible
Angel Pons7c49cb82020-03-16 23:17:32 +0100475 reclaim = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100476 touudbase = tom - me_uma_size;
477 }
478
Angel Pons7c49cb82020-03-16 23:17:32 +0100479 /* Update memory map in PCIe configuration space */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100480 printk(BIOS_DEBUG, "Update PCI-E configuration space:\n");
481
Angel Pons7c49cb82020-03-16 23:17:32 +0100482 /* TOM (top of memory) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100483 reg = pci_read_config32(HOST_BRIDGE, TOM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100484 val = tom & 0xfff;
485 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100486 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100487 pci_write_config32(HOST_BRIDGE, TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100488
Angel Ponsb31d1d72020-01-10 01:35:09 +0100489 reg = pci_read_config32(HOST_BRIDGE, TOM + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100490 val = tom & 0xfffff000;
491 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100492 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100493 pci_write_config32(HOST_BRIDGE, TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100494
Angel Pons7c49cb82020-03-16 23:17:32 +0100495 /* TOLUD (Top Of Low Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100496 reg = pci_read_config32(HOST_BRIDGE, TOLUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100497 val = toludbase & 0xfff;
498 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100499 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOLUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100500 pci_write_config32(HOST_BRIDGE, TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100501
Angel Pons7c49cb82020-03-16 23:17:32 +0100502 /* TOUUD LSB (Top Of Upper Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100503 reg = pci_read_config32(HOST_BRIDGE, TOUUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100504 val = touudbase & 0xfff;
505 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100506 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100507 pci_write_config32(HOST_BRIDGE, TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100508
Angel Pons7c49cb82020-03-16 23:17:32 +0100509 /* TOUUD MSB */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100510 reg = pci_read_config32(HOST_BRIDGE, TOUUD + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100511 val = touudbase & 0xfffff000;
512 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100513 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100514 pci_write_config32(HOST_BRIDGE, TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100515
516 if (reclaim) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100517 /* REMAP BASE */
518 pci_write_config32(HOST_BRIDGE, REMAPBASE, remapbase << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100519 pci_write_config32(HOST_BRIDGE, REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100520
Angel Pons7c49cb82020-03-16 23:17:32 +0100521 /* REMAP LIMIT */
522 pci_write_config32(HOST_BRIDGE, REMAPLIMIT, remaplimit << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100523 pci_write_config32(HOST_BRIDGE, REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100524 }
Angel Pons7c49cb82020-03-16 23:17:32 +0100525 /* TSEG */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100526 reg = pci_read_config32(HOST_BRIDGE, TSEGMB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100527 val = tsegbase & 0xfff;
528 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100529 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TSEGMB, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100530 pci_write_config32(HOST_BRIDGE, TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100531
Angel Pons7c49cb82020-03-16 23:17:32 +0100532 /* GFX stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100533 reg = pci_read_config32(HOST_BRIDGE, BDSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100534 val = gfxstolenbase & 0xfff;
535 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100536 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BDSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100537 pci_write_config32(HOST_BRIDGE, BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100538
Angel Pons7c49cb82020-03-16 23:17:32 +0100539 /* GTT stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100540 reg = pci_read_config32(HOST_BRIDGE, BGSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100541 val = gttbase & 0xfff;
542 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100543 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BGSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100544 pci_write_config32(HOST_BRIDGE, BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100545
546 if (me_uma_size) {
Angel Ponsb31d1d72020-01-10 01:35:09 +0100547 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100548 val = (0x80000 - me_uma_size) & 0xfffff000;
549 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100550 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100551 pci_write_config32(HOST_BRIDGE, MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100552
Angel Pons7c49cb82020-03-16 23:17:32 +0100553 /* ME base */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100554 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100555 val = mestolenbase & 0xfff;
556 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held651f99f2019-12-30 16:28:48 +0100557 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100558 pci_write_config32(HOST_BRIDGE, MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100559
Angel Ponsb31d1d72020-01-10 01:35:09 +0100560 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100561 val = mestolenbase & 0xfffff000;
562 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100563 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100564 pci_write_config32(HOST_BRIDGE, MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100565
Angel Pons7c49cb82020-03-16 23:17:32 +0100566 /* ME mask */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100567 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100568 val = (0x80000 - me_uma_size) & 0xfff;
569 reg = (reg & ~0xfff00000) | (val << 20);
Angel Pons7c49cb82020-03-16 23:17:32 +0100570 reg = reg | ME_STLEN_EN; /* Set ME memory enable */
571 reg = reg | MELCK; /* Set lock bit on ME mem */
Felix Held651f99f2019-12-30 16:28:48 +0100572 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100573 pci_write_config32(HOST_BRIDGE, MESEG_MASK, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100574 }
575}
576
Angel Pons88521882020-01-05 20:21:20 +0100577static void wait_for_iosav(int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100578{
579 while (1) {
Angel Pons88521882020-01-05 20:21:20 +0100580 if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100581 return;
582 }
583}
584
Angel Pons88521882020-01-05 20:21:20 +0100585static void write_reset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100586{
587 int channel, slotrank;
588
Angel Pons7c49cb82020-03-16 23:17:32 +0100589 /* Choose a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100590 channel = (ctrl->rankmap[0]) ? 0 : 1;
591
Angel Pons88521882020-01-05 20:21:20 +0100592 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100593
Angel Pons7c49cb82020-03-16 23:17:32 +0100594 /* Choose a populated rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100595 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
596
597 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200598 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200599 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200600 1, 3, 8, SSQ_NA,
601 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200602 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100603
Angel Pons7c49cb82020-03-16 23:17:32 +0100604 /*
605 * Execute command queue - why is bit 22 set here?!
606 *
607 * This is actually using the IOSAV state machine as a timer, so refresh is allowed.
608 */
Angel Ponsad704002020-05-02 22:51:58 +0200609 iosav_run_queue(channel, 1, 1, true);
Felix Held9cf1dd22018-07-31 14:52:40 +0200610
Angel Pons88521882020-01-05 20:21:20 +0100611 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100612}
613
Angel Pons88521882020-01-05 20:21:20 +0100614void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100615{
Felix Held9fe248f2018-07-31 20:59:45 +0200616 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100617 int channel;
618
Angel Pons7c49cb82020-03-16 23:17:32 +0100619 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
620 ;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100621 do {
Angel Pons88521882020-01-05 20:21:20 +0100622 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100623 } while ((reg & 0x14) == 0);
624
Angel Pons7c49cb82020-03-16 23:17:32 +0100625 /* Set state of memory controller */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100626 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100627 MCHBAR32(MC_INIT_STATE_G) = reg;
628 MCHBAR32(MC_INIT_STATE) = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100629 reg |= 2; /* DDR reset */
Angel Pons88521882020-01-05 20:21:20 +0100630 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100631
Angel Pons7c49cb82020-03-16 23:17:32 +0100632 /* Assert DIMM reset signal */
633 MCHBAR32_AND(MC_INIT_STATE_G, ~2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100634
Angel Pons7c49cb82020-03-16 23:17:32 +0100635 /* Wait 200us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100636 udelay(200);
637
Angel Pons7c49cb82020-03-16 23:17:32 +0100638 /* Deassert DIMM reset signal */
Angel Pons88521882020-01-05 20:21:20 +0100639 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100640
Angel Pons7c49cb82020-03-16 23:17:32 +0100641 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100642 udelay(500);
643
Angel Pons7c49cb82020-03-16 23:17:32 +0100644 /* Enable DCLK */
Angel Pons88521882020-01-05 20:21:20 +0100645 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100646
Angel Pons7c49cb82020-03-16 23:17:32 +0100647 /* XXX Wait 20ns */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100648 udelay(1);
649
650 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100651 /* Set valid rank CKE */
Felix Held9fe248f2018-07-31 20:59:45 +0200652 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100653 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100654
Angel Pons7c49cb82020-03-16 23:17:32 +0100655 /* Wait 10ns for ranks to settle */
656 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100657
658 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100659 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100660
Angel Pons7c49cb82020-03-16 23:17:32 +0100661 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100662 write_reset(ctrl);
663 }
664}
665
666static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
667{
Angel Pons7c49cb82020-03-16 23:17:32 +0100668 /* Get ODT based on rankmap */
669 int dimms_per_ch = (ctrl->rankmap[channel] & 1) + ((ctrl->rankmap[channel] >> 2) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100670
671 if (dimms_per_ch == 1) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100672 return (const odtmap){60, 60};
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100673 } else {
674 return (const odtmap){120, 30};
675 }
676}
677
Angel Pons7c49cb82020-03-16 23:17:32 +0100678static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank, int reg, u32 val)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100679{
Angel Pons88521882020-01-05 20:21:20 +0100680 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100681
682 if (ctrl->rank_mirror[channel][slotrank]) {
683 /* DDR3 Rank1 Address mirror
Angel Pons7c49cb82020-03-16 23:17:32 +0100684 swap the following pins:
685 A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100686 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
Angel Pons7c49cb82020-03-16 23:17:32 +0100687 val = (val & ~0x1f8) | ((val >> 1) & 0xa8) | ((val & 0xa8) << 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100688 }
689
690 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200691 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200692 IOSAV_MRS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200693 1, 4, 4, SSQ_NA,
694 val, 6, reg, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200695 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100696
697 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200698 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +0200699 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +0200700 1, 4, 4, SSQ_NA,
701 val, 6, reg, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200702 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100703
704 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200705 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +0200706 IOSAV_MRS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200707 1, 4, ctrl->tMOD, SSQ_NA,
708 val, 6, reg, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200709 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +0200710
Angel Pons7c49cb82020-03-16 23:17:32 +0100711 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +0200712 iosav_run_once(channel, 3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100713}
714
Angel Pons88521882020-01-05 20:21:20 +0100715static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100716{
717 u16 mr0reg, mch_cas, mch_wr;
718 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 +0100719 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100720
721 /* DLL Reset - self clearing - set after CLK frequency has been changed */
722 mr0reg = 0x100;
723
Angel Pons7c49cb82020-03-16 23:17:32 +0100724 /* Convert CAS to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100725 if (ctrl->CAS < 12) {
726 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
727 } else {
728 mch_cas = (u16) (ctrl->CAS - 12);
729 mch_cas = ((mch_cas << 1) | 0x1);
730 }
731
Angel Pons7c49cb82020-03-16 23:17:32 +0100732 /* Convert tWR to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100733 mch_wr = mch_wr_t[ctrl->tWR - 5];
734
Angel Pons7c49cb82020-03-16 23:17:32 +0100735 mr0reg = (mr0reg & ~0x0004) | ((mch_cas & 0x1) << 2);
736 mr0reg = (mr0reg & ~0x0070) | ((mch_cas & 0xe) << 3);
737 mr0reg = (mr0reg & ~0x0e00) | (mch_wr << 9);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100738
Angel Pons7c49cb82020-03-16 23:17:32 +0100739 /* Precharge PD - Fast (desktop) 1 or slow (mobile) 0 - mostly power-saving feature */
740 mr0reg = (mr0reg & ~(1 << 12)) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100741 return mr0reg;
742}
743
744static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
745{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200746 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100747}
748
749static u32 encode_odt(u32 odt)
750{
751 switch (odt) {
752 case 30:
753 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
754 case 60:
755 return (1 << 2); // RZQ/4
756 case 120:
757 return (1 << 6); // RZQ/2
758 default:
759 case 0:
760 return 0;
761 }
762}
763
764static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
765{
766 odtmap odt;
767 u32 mr1reg;
768
769 odt = get_ODT(ctrl, rank, channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100770 mr1reg = 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100771
772 mr1reg |= encode_odt(odt.rttnom);
773
774 return mr1reg;
775}
776
777static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
778{
779 u16 mr1reg;
780
781 mr1reg = make_mr1(ctrl, rank, channel);
782
783 write_mrreg(ctrl, channel, rank, 1, mr1reg);
784}
785
786static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
787{
788 u16 pasr, cwl, mr2reg;
789 odtmap odt;
790 int srt;
791
792 pasr = 0;
793 cwl = ctrl->CWL - 5;
794 odt = get_ODT(ctrl, rank, channel);
795
796 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
797
798 mr2reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100799 mr2reg = (mr2reg & ~0x07) | pasr;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100800 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
801 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
802 mr2reg = (mr2reg & ~0x80) | (srt << 7);
803 mr2reg |= (odt.rttwr / 60) << 9;
804
805 write_mrreg(ctrl, channel, rank, 2, mr2reg);
806}
807
808static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
809{
810 write_mrreg(ctrl, channel, rank, 3, 0);
811}
812
Angel Pons88521882020-01-05 20:21:20 +0100813void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100814{
815 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100816 int channel;
817
818 FOR_ALL_POPULATED_CHANNELS {
819 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100820 /* MR2 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100821 dram_mr2(ctrl, slotrank, channel);
822
Angel Pons7c49cb82020-03-16 23:17:32 +0100823 /* MR3 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100824 dram_mr3(ctrl, slotrank, channel);
825
Angel Pons7c49cb82020-03-16 23:17:32 +0100826 /* MR1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100827 dram_mr1(ctrl, slotrank, channel);
828
Angel Pons7c49cb82020-03-16 23:17:32 +0100829 /* MR0 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100830 dram_mr0(ctrl, slotrank, channel);
831 }
832 }
833
Angel Pons69e17142020-03-23 12:26:29 +0100834 /* DRAM command NOP (without ODT nor chip selects) */
Angel Ponsca00dec2020-05-02 15:04:00 +0200835 IOSAV_SUBSEQUENCE(BROADCAST_CH, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200836 IOSAV_NOP & ~(0xff << 8), 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200837 1, 4, 15, SSQ_NA,
838 2, 6, 0, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200839 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100840
841 /* DRAM command ZQCL */
Angel Ponsca00dec2020-05-02 15:04:00 +0200842 IOSAV_SUBSEQUENCE(BROADCAST_CH, 1,
Angel Ponsb631d072020-05-02 20:00:32 +0200843 IOSAV_ZQCS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +0200844 1, 4, 400, SSQ_NA,
845 1024, 6, 0, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200846 0, 0, 0, 1, 20, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100847
Angel Pons7c49cb82020-03-16 23:17:32 +0100848 /* Execute command queue on all channels. Do it four times. */
Angel Ponsad704002020-05-02 22:51:58 +0200849 iosav_run_queue(BROADCAST_CH, 4, 2, false);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100850
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100851 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100852 /* Wait for ref drained */
Angel Pons88521882020-01-05 20:21:20 +0100853 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100854 }
855
Angel Pons7c49cb82020-03-16 23:17:32 +0100856 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +0100857 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100858
859 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +0100860 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100861
Angel Pons88521882020-01-05 20:21:20 +0100862 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100863
864 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
865
Angel Pons7c49cb82020-03-16 23:17:32 +0100866 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100867 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100868
869 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200870 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200871 IOSAV_ZQCS, 0,
Angel Pons2be59002020-05-02 22:15:03 +0200872 1, 4, 101, SSQ_NA,
Angel Ponsca00dec2020-05-02 15:04:00 +0200873 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200874 0, 0, 0, 0, 31, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +0200875
Angel Pons7c49cb82020-03-16 23:17:32 +0100876 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +0200877 iosav_run_once(channel, 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100878
Angel Pons7c49cb82020-03-16 23:17:32 +0100879 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100880 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100881 }
882}
883
Felix Held3b906032020-01-14 17:05:43 +0100884static const u32 lane_base[] = {
885 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
886 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
887 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100888};
889
Angel Pons88521882020-01-05 20:21:20 +0100890void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100891{
Angel Pons88521882020-01-05 20:21:20 +0100892 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100893 int lane;
894 int slotrank, slot;
895 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +0100896 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100897
898 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +0100899 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
900 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100901 }
902
903 for (slot = 0; slot < NUM_SLOTS; slot++)
904 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
905 case 0:
906 default:
Angel Pons88521882020-01-05 20:21:20 +0100907 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100908 break;
909 case 1:
Angel Pons88521882020-01-05 20:21:20 +0100910 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100911 ctrl->timings[channel][2 * slot + 0].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100912 break;
913 case 2:
Angel Pons88521882020-01-05 20:21:20 +0100914 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100915 ctrl->timings[channel][2 * slot + 1].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100916 break;
917 case 3:
Angel Pons88521882020-01-05 20:21:20 +0100918 pi_coding_ctrl[slot] =
919 (ctrl->timings[channel][2 * slot].pi_coding +
Angel Pons7c49cb82020-03-16 23:17:32 +0100920 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100921 break;
922 }
923
Angel Pons7c49cb82020-03-16 23:17:32 +0100924 /* Enable CMD XOVER */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100925 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons7c49cb82020-03-16 23:17:32 +0100926 reg32 |= (pi_coding_ctrl[0] & 0x3f) << 6;
927 reg32 |= (pi_coding_ctrl[0] & 0x40) << 9;
Angel Pons88521882020-01-05 20:21:20 +0100928 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100929 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
930
Angel Pons88521882020-01-05 20:21:20 +0100931 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100932
Angel Pons7c49cb82020-03-16 23:17:32 +0100933 /* Enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +0100934 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
935 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100936
937 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100938 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Angel Pons88521882020-01-05 20:21:20 +0100939 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100940 if (shift < 0)
941 shift = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100942
Angel Pons88521882020-01-05 20:21:20 +0100943 offset_pi_code = ctrl->pi_code_offset + shift;
Angel Pons7c49cb82020-03-16 23:17:32 +0100944
945 /* Set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +0100946 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
947 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100948 }
949
Angel Pons88521882020-01-05 20:21:20 +0100950 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
951 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100952
Angel Pons88521882020-01-05 20:21:20 +0100953 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +0100954 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100955
Angel Pons88521882020-01-05 20:21:20 +0100956 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100957
958 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100959 int post_timA_min_high = 7, pre_timA_min_high = 7;
960 int post_timA_max_high = 0, pre_timA_max_high = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100961 int shift_402x = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100962 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100963
964 if (shift < 0)
965 shift = 0;
966
967 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +0200968 post_timA_min_high = MIN(post_timA_min_high,
969 (ctrl->timings[channel][slotrank].lanes[lane].
970 timA + shift) >> 6);
971 pre_timA_min_high = MIN(pre_timA_min_high,
972 ctrl->timings[channel][slotrank].lanes[lane].
973 timA >> 6);
974 post_timA_max_high = MAX(post_timA_max_high,
975 (ctrl->timings[channel][slotrank].lanes[lane].
976 timA + shift) >> 6);
977 pre_timA_max_high = MAX(pre_timA_max_high,
978 ctrl->timings[channel][slotrank].lanes[lane].
979 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100980 }
981
982 if (pre_timA_max_high - pre_timA_min_high <
983 post_timA_max_high - post_timA_min_high)
984 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +0100985
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100986 else if (pre_timA_max_high - pre_timA_min_high >
987 post_timA_max_high - post_timA_min_high)
988 shift_402x = -1;
989
Felix Helddee167e2019-12-30 17:30:16 +0100990 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +0100991 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100992 post_timA_min_high) << (4 * slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +0100993
Angel Pons88521882020-01-05 20:21:20 +0100994 reg_roundtrip_latency |=
995 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100996 shift_402x) << (8 * slotrank);
997
998 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +0100999 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001000 (((ctrl->timings[channel][slotrank].lanes[lane].
1001 timA + shift) & 0x3f)
1002 |
1003 ((ctrl->timings[channel][slotrank].lanes[lane].
1004 rising + shift) << 8)
1005 |
1006 (((ctrl->timings[channel][slotrank].lanes[lane].
1007 timA + shift -
1008 (post_timA_min_high << 6)) & 0x1c0) << 10)
1009 | ((ctrl->timings[channel][slotrank].lanes[lane].
1010 falling + shift) << 20));
1011
Felix Heldfb19c8a2020-01-14 21:27:59 +01001012 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001013 (((ctrl->timings[channel][slotrank].lanes[lane].
1014 timC + shift) & 0x3f)
1015 |
1016 (((ctrl->timings[channel][slotrank].lanes[lane].
1017 timB + shift) & 0x3f) << 8)
1018 |
1019 (((ctrl->timings[channel][slotrank].lanes[lane].
1020 timB + shift) & 0x1c0) << 9)
1021 |
1022 (((ctrl->timings[channel][slotrank].lanes[lane].
1023 timC + shift) & 0x40) << 13));
1024 }
1025 }
Angel Pons88521882020-01-05 20:21:20 +01001026 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1027 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001028}
1029
Angel Pons88521882020-01-05 20:21:20 +01001030static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001031{
Angel Pons88521882020-01-05 20:21:20 +01001032 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001033
1034 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001035 write MR3 MPR enable
1036 in this mode only RD and RDA are allowed
1037 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001038 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001039 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001040 1, 3, ctrl->tMOD, SSQ_NA,
1041 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001042 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001043
1044 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001045 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001046 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001047 1, 3, 4, SSQ_RD,
1048 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001049 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001050
1051 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001052 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001053 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001054 15, 4, ctrl->CAS + 36, SSQ_NA,
1055 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001056 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001057
1058 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001059 write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001060 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001061 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001062 1, 3, ctrl->tMOD, SSQ_NA,
1063 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001064 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001065
Angel Pons7c49cb82020-03-16 23:17:32 +01001066 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001067 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001068
Angel Pons88521882020-01-05 20:21:20 +01001069 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001070}
1071
Angel Pons7c49cb82020-03-16 23:17:32 +01001072static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank, int lane)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001073{
1074 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Angel Pons7c49cb82020-03-16 23:17:32 +01001075
1076 return (MCHBAR32(lane_base[lane] +
1077 GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >> (timA % 32)) & 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001078}
1079
1080struct run {
1081 int middle;
1082 int end;
1083 int start;
1084 int all;
1085 int length;
1086};
1087
1088static struct run get_longest_zero_run(int *seq, int sz)
1089{
1090 int i, ls;
1091 int bl = 0, bs = 0;
1092 struct run ret;
1093
1094 ls = 0;
1095 for (i = 0; i < 2 * sz; i++)
1096 if (seq[i % sz]) {
1097 if (i - ls > bl) {
1098 bl = i - ls;
1099 bs = ls;
1100 }
1101 ls = i + 1;
1102 }
1103 if (bl == 0) {
1104 ret.middle = sz / 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001105 ret.start = 0;
1106 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001107 ret.length = sz;
Angel Pons7c49cb82020-03-16 23:17:32 +01001108 ret.all = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001109 return ret;
1110 }
1111
Angel Pons7c49cb82020-03-16 23:17:32 +01001112 ret.start = bs % sz;
1113 ret.end = (bs + bl - 1) % sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001114 ret.middle = (bs + (bl - 1) / 2) % sz;
1115 ret.length = bl;
Angel Pons7c49cb82020-03-16 23:17:32 +01001116 ret.all = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001117
1118 return ret;
1119}
1120
Angel Pons7c49cb82020-03-16 23:17:32 +01001121static void discover_timA_coarse(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001122{
1123 int timA;
1124 int statistics[NUM_LANES][128];
1125 int lane;
1126
1127 for (timA = 0; timA < 128; timA++) {
1128 FOR_ALL_LANES {
1129 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1130 }
1131 program_timings(ctrl, channel);
1132
1133 test_timA(ctrl, channel, slotrank);
1134
1135 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001136 statistics[lane][timA] = !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001137 }
1138 }
1139 FOR_ALL_LANES {
1140 struct run rn = get_longest_zero_run(statistics[lane], 128);
1141 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1142 upperA[lane] = rn.end;
1143 if (upperA[lane] < rn.middle)
1144 upperA[lane] += 128;
Angel Pons7c49cb82020-03-16 23:17:32 +01001145
Patrick Rudolph368b6152016-11-25 16:36:52 +01001146 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001147 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001148 }
1149}
1150
Angel Pons7c49cb82020-03-16 23:17:32 +01001151static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001152{
1153 int timA_delta;
1154 int statistics[NUM_LANES][51];
1155 int lane, i;
1156
1157 memset(statistics, 0, sizeof(statistics));
1158
1159 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01001160
1161 FOR_ALL_LANES {
1162 ctrl->timings[channel][slotrank].lanes[lane].timA
1163 = upperA[lane] + timA_delta + 0x40;
1164 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001165 program_timings(ctrl, channel);
1166
1167 for (i = 0; i < 100; i++) {
1168 test_timA(ctrl, channel, slotrank);
1169 FOR_ALL_LANES {
1170 statistics[lane][timA_delta + 25] +=
Angel Pons7c49cb82020-03-16 23:17:32 +01001171 does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001172 }
1173 }
1174 }
1175 FOR_ALL_LANES {
1176 int last_zero, first_all;
1177
1178 for (last_zero = -25; last_zero <= 25; last_zero++)
1179 if (statistics[lane][last_zero + 25])
1180 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01001181
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001182 last_zero--;
1183 for (first_all = -25; first_all <= 25; first_all++)
1184 if (statistics[lane][first_all + 25] == 100)
1185 break;
1186
Angel Pons7c49cb82020-03-16 23:17:32 +01001187 printram("lane %d: %d, %d\n", lane, last_zero, first_all);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001188
1189 ctrl->timings[channel][slotrank].lanes[lane].timA =
Angel Pons7c49cb82020-03-16 23:17:32 +01001190 (last_zero + first_all) / 2 + upperA[lane];
1191
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001192 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01001193 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001194 }
1195}
1196
Angel Pons891f2bc2020-01-10 01:27:28 +01001197static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001198{
1199 int works[NUM_LANES];
1200 int lane;
Angel Pons7c49cb82020-03-16 23:17:32 +01001201
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001202 while (1) {
1203 int all_works = 1, some_works = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001204
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001205 program_timings(ctrl, channel);
1206 test_timA(ctrl, channel, slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001207
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001208 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001209 works[lane] = !does_lane_work(ctrl, channel, slotrank, lane);
1210
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001211 if (works[lane])
1212 some_works = 1;
1213 else
1214 all_works = 0;
1215 }
1216 if (all_works)
1217 return 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001218
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001219 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001220 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001221 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1222 channel, slotrank);
1223 return MAKE_ERR;
1224 }
Angel Pons88521882020-01-05 20:21:20 +01001225 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001226 printram("4024 -= 2;\n");
1227 continue;
1228 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001229 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001230 printram("4028 += 2;\n");
Angel Pons7c49cb82020-03-16 23:17:32 +01001231
Felix Heldef4fe3e2019-12-31 14:15:05 +01001232 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001233 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1234 channel, slotrank);
1235 return MAKE_ERR;
1236 }
1237 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001238 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001239 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001240 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001241 }
1242 }
1243 return 0;
1244}
1245
1246struct timA_minmax {
1247 int timA_min_high, timA_max_high;
1248};
1249
Angel Pons88521882020-01-05 20:21:20 +01001250static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001251 struct timA_minmax *mnmx)
1252{
1253 int lane;
1254 mnmx->timA_min_high = 7;
1255 mnmx->timA_max_high = 0;
1256
1257 FOR_ALL_LANES {
1258 if (mnmx->timA_min_high >
1259 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1260 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001261 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001262 if (mnmx->timA_max_high <
1263 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1264 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001265 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001266 }
1267}
1268
Angel Pons88521882020-01-05 20:21:20 +01001269static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001270 struct timA_minmax *mnmx)
1271{
1272 struct timA_minmax post;
1273 int shift_402x = 0;
1274
Angel Pons7c49cb82020-03-16 23:17:32 +01001275 /* Get changed maxima */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001276 pre_timA_change(ctrl, channel, slotrank, &post);
1277
1278 if (mnmx->timA_max_high - mnmx->timA_min_high <
1279 post.timA_max_high - post.timA_min_high)
1280 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001281
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001282 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1283 post.timA_max_high - post.timA_min_high)
1284 shift_402x = -1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001285
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001286 else
1287 shift_402x = 0;
1288
Felix Heldef4fe3e2019-12-31 14:15:05 +01001289 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001290 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001291 printram("4024 += %d;\n", shift_402x);
1292 printram("4028 += %d;\n", shift_402x);
1293}
1294
Angel Pons7c49cb82020-03-16 23:17:32 +01001295/*
1296 * Compensate the skew between DQS and DQs.
1297 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001298 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1299 * The controller has to measure and compensate this skew for every byte-lane. By delaying
Angel Pons7c49cb82020-03-16 23:17:32 +01001300 * either all DQ signals or DQS signal, a full phase shift can be introduced. It is assumed
Angel Pons891f2bc2020-01-10 01:27:28 +01001301 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001302 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001303 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1304 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1305 * over all possible values to do a full phase shift and issues read commands. With DQS and
Angel Pons7c49cb82020-03-16 23:17:32 +01001306 * DQ in phase the data being read is expected to alternate on every byte:
1307 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001308 * 0xFF 0x00 0xFF ...
Angel Pons7c49cb82020-03-16 23:17:32 +01001309 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001310 * Once the controller has detected this pattern a bit in the result register is set for the
1311 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001312 */
Angel Pons88521882020-01-05 20:21:20 +01001313int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001314{
1315 int channel, slotrank, lane;
1316 int err;
1317
1318 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1319 int all_high, some_high;
1320 int upperA[NUM_LANES];
1321 struct timA_minmax mnmx;
1322
Angel Pons88521882020-01-05 20:21:20 +01001323 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001324
Felix Held2bb3cdf2018-07-28 00:23:59 +02001325 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001326 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001327 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001328 1, 3, ctrl->tRP, SSQ_NA,
1329 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001330 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001331
Angel Pons7c49cb82020-03-16 23:17:32 +01001332 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001333 iosav_run_once(channel, 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001334
Angel Pons88521882020-01-05 20:21:20 +01001335 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001336
Felix Heldef4fe3e2019-12-31 14:15:05 +01001337 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001338 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001339 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001340
Felix Held2bb3cdf2018-07-28 00:23:59 +02001341 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001342
Felix Held2bb3cdf2018-07-28 00:23:59 +02001343 all_high = 1;
1344 some_high = 0;
1345 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001346 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001347 some_high = 1;
1348 else
1349 all_high = 0;
1350 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001351
1352 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001353 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001354 printram("4028--;\n");
1355 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001356 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001357 upperA[lane] -= 0x40;
1358
1359 }
1360 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001361 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001362 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001363 printram("4024++;\n");
1364 printram("4028++;\n");
1365 }
1366
1367 program_timings(ctrl, channel);
1368
1369 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1370
1371 err = discover_402x(ctrl, channel, slotrank, upperA);
1372 if (err)
1373 return err;
1374
1375 post_timA_change(ctrl, channel, slotrank, &mnmx);
1376 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1377
1378 discover_timA_fine(ctrl, channel, slotrank, upperA);
1379
1380 post_timA_change(ctrl, channel, slotrank, &mnmx);
1381 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1382
1383 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001384 ctrl->timings[channel][slotrank].lanes[lane].timA -=
1385 mnmx.timA_min_high * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001386 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001387 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001388 printram("4028 -= %d;\n", mnmx.timA_min_high);
1389
1390 post_timA_change(ctrl, channel, slotrank, &mnmx);
1391
1392 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001393 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001394 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001395
1396 printram("final results:\n");
1397 FOR_ALL_LANES
Angel Pons7c49cb82020-03-16 23:17:32 +01001398 printram("Aval: %d, %d, %d: %x\n", channel, slotrank, lane,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001399 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001400
Angel Pons88521882020-01-05 20:21:20 +01001401 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001402
1403 toggle_io_reset();
1404 }
1405
1406 FOR_ALL_POPULATED_CHANNELS {
1407 program_timings(ctrl, channel);
1408 }
1409 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001410 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001411 }
1412 return 0;
1413}
1414
Angel Pons88521882020-01-05 20:21:20 +01001415static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001416{
1417 int lane;
1418
1419 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001420 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1421 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001422 }
1423
Angel Pons88521882020-01-05 20:21:20 +01001424 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001425
1426 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001427 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001428 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001429 4, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
1430 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001431 0, 0, 1, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001432
1433 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001434 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001435 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001436 1, 4, 4, SSQ_WR,
1437 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001438 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001439
1440 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02001441 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001442 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001443 500, 4, 4, SSQ_WR,
1444 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001445 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001446
1447 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001448 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001449 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001450 1, 3, ctrl->CWL + ctrl->tWTR + 5, SSQ_WR,
1451 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001452 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001453
Angel Pons7c49cb82020-03-16 23:17:32 +01001454 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001455 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001456
Angel Pons88521882020-01-05 20:21:20 +01001457 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001458
1459 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001460 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001461 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001462 1, 3, ctrl->tRP, SSQ_NA,
1463 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001464 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001465
1466 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001467 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001468 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001469 8, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->CAS, SSQ_NA,
1470 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001471 0, 0, 1, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001472
1473 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001474 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001475 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001476 500, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
1477 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001478 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001479
1480 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001481 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001482 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001483 1, 3, ctrl->tRP, SSQ_NA,
1484 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001485 0, 0, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001486
Angel Pons7c49cb82020-03-16 23:17:32 +01001487 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001488 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02001489
Angel Pons88521882020-01-05 20:21:20 +01001490 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001491}
1492
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001493static void timC_threshold_process(int *data, const int count)
1494{
1495 int min = data[0];
1496 int max = min;
1497 int i;
1498 for (i = 1; i < count; i++) {
1499 if (min > data[i])
1500 min = data[i];
Angel Pons7c49cb82020-03-16 23:17:32 +01001501
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001502 if (max < data[i])
1503 max = data[i];
1504 }
Angel Pons7c49cb82020-03-16 23:17:32 +01001505 int threshold = min / 2 + max / 2;
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001506 for (i = 0; i < count; i++)
1507 data[i] = data[i] > threshold;
Angel Pons7c49cb82020-03-16 23:17:32 +01001508
Angel Pons891f2bc2020-01-10 01:27:28 +01001509 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001510}
1511
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001512static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1513{
1514 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01001515 int stats[NUM_LANES][MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001516 int lane;
1517
Angel Pons88521882020-01-05 20:21:20 +01001518 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001519
1520 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001521 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001522 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001523 1, 3, ctrl->tRP, SSQ_NA,
1524 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001525 0, 0, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001526
Angel Pons7c49cb82020-03-16 23:17:32 +01001527 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001528 iosav_run_once(channel, 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001529
1530 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001531 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001532 program_timings(ctrl, channel);
1533
1534 test_timC(ctrl, channel, slotrank);
1535
1536 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001537 stats[lane][timC] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001538 }
1539 }
1540 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001541 struct run rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1542
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001543 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001544 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1545 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001546 /*
1547 * With command training not being done yet, the lane can be erroneous.
1548 * Take the average as reference and try again to find a run.
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001549 */
Angel Pons7c49cb82020-03-16 23:17:32 +01001550 timC_threshold_process(stats[lane], ARRAY_SIZE(stats[lane]));
1551 rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1552
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001553 if (rn.all || rn.length < 8) {
1554 printk(BIOS_EMERG, "timC recovery failed\n");
1555 return MAKE_ERR;
1556 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001557 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001558 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001559 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001560 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001561 }
1562 return 0;
1563}
1564
Angel Pons88521882020-01-05 20:21:20 +01001565static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001566{
1567 int channel, ret = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001568
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001569 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1570 ret++;
Angel Pons7c49cb82020-03-16 23:17:32 +01001571
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001572 return ret;
1573}
1574
Angel Pons88521882020-01-05 20:21:20 +01001575static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001576{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301577 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001578 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Angel Pons7c49cb82020-03-16 23:17:32 +01001579
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001580 for (j = 0; j < 16; j++)
1581 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
Angel Pons7c49cb82020-03-16 23:17:32 +01001582
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001583 sfence();
1584}
1585
Angel Pons88521882020-01-05 20:21:20 +01001586static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001587{
1588 int ret = 0;
1589 int channel;
1590 FOR_ALL_POPULATED_CHANNELS ret++;
1591 return ret;
1592}
1593
Angel Pons88521882020-01-05 20:21:20 +01001594static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001595{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301596 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001597 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301598 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01001599
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001600 for (j = 0; j < 16; j++)
1601 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
Angel Pons7c49cb82020-03-16 23:17:32 +01001602
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001603 for (j = 0; j < 16; j++)
1604 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
Angel Pons7c49cb82020-03-16 23:17:32 +01001605
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001606 sfence();
1607}
1608
Angel Pons88521882020-01-05 20:21:20 +01001609static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001610{
1611 int channel, slotrank, lane;
1612
1613 FOR_ALL_POPULATED_CHANNELS {
1614 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001615 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1616 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001617 }
1618
1619 program_timings(ctrl, channel);
1620
1621 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001622 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001623
1624 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001625 write MR3 MPR enable
1626 in this mode only RD and RDA are allowed
1627 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001628 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001629 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001630 1, 3, ctrl->tMOD, SSQ_NA,
1631 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001632 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001633
1634 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001635 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001636 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001637 3, 4, 4, SSQ_RD,
1638 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001639 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001640
1641 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001642 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001643 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001644 1, 4, ctrl->CAS + 8, SSQ_NA,
1645 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001646 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001647
1648 /* DRAM command MRS
1649 * write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001650 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001651 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001652 1, 3, ctrl->tMOD, SSQ_NA,
1653 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001654 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001655
Angel Pons7c49cb82020-03-16 23:17:32 +01001656 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001657 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001658
Angel Pons88521882020-01-05 20:21:20 +01001659 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001660 }
1661
1662 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001663 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1664 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001665 }
1666
1667 program_timings(ctrl, channel);
1668
1669 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001670 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001671 /* DRAM command MRS
1672 * write MR3 MPR enable
1673 * in this mode only RD and RDA are allowed
1674 * all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001675 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001676 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001677 1, 3, ctrl->tMOD, SSQ_NA,
1678 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001679 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001680
1681 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001682 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001683 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001684 3, 4, 4, SSQ_RD,
1685 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001686 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001687
1688 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001689 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001690 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001691 1, 4, ctrl->CAS + 8, SSQ_NA,
1692 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001693 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001694
1695 /* DRAM command MRS
1696 * write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001697 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001698 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001699 1, 3, ctrl->tMOD, SSQ_NA,
1700 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001701 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001702
Angel Pons7c49cb82020-03-16 23:17:32 +01001703 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001704 iosav_run_once(channel, 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 Ponsca00dec2020-05-02 15:04:00 +02001718 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001719 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001720 1, 3, ctrl->CWL + ctrl->tWLO, SSQ_WR,
1721 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001722 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001723
1724 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001725 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001726 IOSAV_NOP_ALT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001727 1, 3, ctrl->CAS + 38, SSQ_RD,
1728 4, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001729 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001730
Angel Pons7c49cb82020-03-16 23:17:32 +01001731 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001732 iosav_run_once(channel, 2);
Felix Held9cf1dd22018-07-31 14:52:40 +02001733
Angel Pons88521882020-01-05 20:21:20 +01001734 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001735
1736 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001737 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001738}
1739
1740static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1741{
1742 int timB;
1743 int statistics[NUM_LANES][128];
1744 int lane;
1745
Angel Pons88521882020-01-05 20:21:20 +01001746 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001747
1748 for (timB = 0; timB < 128; timB++) {
1749 FOR_ALL_LANES {
1750 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1751 }
1752 program_timings(ctrl, channel);
1753
1754 test_timB(ctrl, channel, slotrank);
1755
1756 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001757 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1758 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1759 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001760 }
1761 }
1762 FOR_ALL_LANES {
1763 struct run rn = get_longest_zero_run(statistics[lane], 128);
Angel Pons7c49cb82020-03-16 23:17:32 +01001764 /*
1765 * timC is a direct function of timB's 6 LSBs. Some tests increments the value
1766 * of timB by a small value, which might cause the 6-bit value to overflow if
1767 * it's close to 0x3f. Increment the value by a small offset if it's likely
1768 * to overflow, to make sure it won't overflow while running tests and bricks
1769 * the system due to a non matching timC.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001770 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001771 * TODO: find out why some tests (edge write discovery) increment timB.
1772 */
1773 if ((rn.start & 0x3f) == 0x3e)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001774 rn.start += 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001775 else if ((rn.start & 0x3f) == 0x3f)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001776 rn.start += 1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001777
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001778 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1779 if (rn.all) {
1780 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1781 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001782
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001783 return MAKE_ERR;
1784 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001785 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1786 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001787 }
1788 return 0;
1789}
1790
1791static int get_timB_high_adjust(u64 val)
1792{
1793 int i;
1794
1795 /* good */
1796 if (val == 0xffffffffffffffffLL)
1797 return 0;
1798
1799 if (val >= 0xf000000000000000LL) {
1800 /* needs negative adjustment */
1801 for (i = 0; i < 8; i++)
1802 if (val << (8 * (7 - i) + 4))
1803 return -i;
1804 } else {
1805 /* needs positive adjustment */
1806 for (i = 0; i < 8; i++)
1807 if (val >> (8 * (7 - i) + 4))
1808 return i;
1809 }
1810 return 8;
1811}
1812
Angel Pons88521882020-01-05 20:21:20 +01001813static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001814{
1815 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001816 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001817 FOR_ALL_POPULATED_CHANNELS {
1818 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001819 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001820 }
1821 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1822
Angel Pons88521882020-01-05 20:21:20 +01001823 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001824
Angel Pons88521882020-01-05 20:21:20 +01001825 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001826
1827 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001828 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001829 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001830 1, 3, ctrl->tRCD, SSQ_NA,
1831 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001832 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001833
1834 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001835 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001836 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001837 1, 3, 4, SSQ_WR,
1838 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001839 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001840
1841 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02001842 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001843 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001844 3, 4, 4, SSQ_WR,
1845 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001846 0, 1, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001847
1848 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001849 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001850 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001851 1, 3, ctrl->CWL + ctrl->tWTR + 5, SSQ_WR,
1852 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001853 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001854
Angel Pons7c49cb82020-03-16 23:17:32 +01001855 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001856 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001857
Angel Pons88521882020-01-05 20:21:20 +01001858 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001859
1860 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001861 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001862 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001863 1, 3, ctrl->tRP, SSQ_NA,
1864 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001865 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001866
1867 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001868 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001869 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001870 1, 3, ctrl->tRCD, SSQ_NA,
1871 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001872 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001873
1874 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001875 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001876 IOSAV_RD, 3,
Angel Ponsca00dec2020-05-02 15:04:00 +02001877 1, 3, ctrl->tRP +
1878 ctrl->timings[channel][slotrank].roundtrip_latency +
1879 ctrl->timings[channel][slotrank].io_latency, SSQ_RD,
1880 8, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001881 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001882
Angel Pons7c49cb82020-03-16 23:17:32 +01001883 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001884 iosav_run_once(channel, 3);
Felix Held9cf1dd22018-07-31 14:52:40 +02001885
Angel Pons88521882020-01-05 20:21:20 +01001886 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001887 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001888 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01001889 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01001890 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001891 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
1892 ctrl->timings[channel][slotrank].lanes[lane].timB +=
1893 get_timB_high_adjust(res) * 64;
1894
1895 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01001896 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
1897 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001898 }
1899 }
Angel Pons88521882020-01-05 20:21:20 +01001900 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001901}
1902
Angel Pons88521882020-01-05 20:21:20 +01001903static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001904{
1905 int slotrank;
1906
Angel Pons88521882020-01-05 20:21:20 +01001907 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001908
1909 /* choose an existing rank. */
1910 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
1911
Angel Pons69e17142020-03-23 12:26:29 +01001912 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02001913 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001914 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02001915 1, 4, 4, SSQ_NA,
1916 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001917 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001918
Angel Pons7c49cb82020-03-16 23:17:32 +01001919 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001920 iosav_run_once(channel, 1);
Felix Held9cf1dd22018-07-31 14:52:40 +02001921
Angel Pons88521882020-01-05 20:21:20 +01001922 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001923}
1924
Angel Pons7c49cb82020-03-16 23:17:32 +01001925/*
1926 * Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001927 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001928 * Since DDR3 uses a fly-by topology, the data and strobes signals reach the chips at different
1929 * times with respect to command, address and clock signals. By delaying either all DQ/DQS or
1930 * all CMD/ADDR/CLK signals, a full phase shift can be introduced. It is assumed that the
1931 * CLK/ADDR/CMD signals have the same routing delay.
1932 *
1933 * To find the required phase shift the DRAM is placed in "write leveling" mode. In this mode,
1934 * the DRAM-chip samples the CLK on every DQS edge and feeds back the sampled value on the data
1935 * lanes (DQ).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001936 */
Angel Pons88521882020-01-05 20:21:20 +01001937int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001938{
1939 int channel, slotrank, lane;
1940 int err;
1941
1942 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001943 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001944
1945 FOR_ALL_POPULATED_CHANNELS {
1946 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001947 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001948 }
1949
Angel Pons7c49cb82020-03-16 23:17:32 +01001950 /* Refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01001951 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001952 FOR_ALL_POPULATED_CHANNELS {
1953 write_op(ctrl, channel);
1954 }
1955
Angel Pons7c49cb82020-03-16 23:17:32 +01001956 /* Enable write leveling on all ranks
1957 Disable all DQ outputs
1958 Only NOP is allowed in this mode */
1959 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
1960 write_mrreg(ctrl, channel, slotrank, 1,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001961 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001962
Angel Pons88521882020-01-05 20:21:20 +01001963 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001964
1965 toggle_io_reset();
1966
Angel Pons7c49cb82020-03-16 23:17:32 +01001967 /* Set any valid value for timB, it gets corrected later */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001968 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1969 err = discover_timB(ctrl, channel, slotrank);
1970 if (err)
1971 return err;
1972 }
1973
Angel Pons7c49cb82020-03-16 23:17:32 +01001974 /* Disable write leveling on all ranks */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001975 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
Angel Pons7c49cb82020-03-16 23:17:32 +01001976 write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001977
Angel Pons88521882020-01-05 20:21:20 +01001978 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001979
1980 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001981 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001982
Angel Pons7c49cb82020-03-16 23:17:32 +01001983 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01001984 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001985
1986 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001987 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
1988 MCHBAR32(IOSAV_STATUS_ch(channel));
1989 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001990
1991 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02001992 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001993 IOSAV_ZQCS, 0,
Angel Pons2be59002020-05-02 22:15:03 +02001994 1, 4, 101, SSQ_NA,
Angel Ponsca00dec2020-05-02 15:04:00 +02001995 0, 6, 0, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001996 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001997
Angel Pons7c49cb82020-03-16 23:17:32 +01001998 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02001999 iosav_run_once(channel, 1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002000
Angel Pons88521882020-01-05 20:21:20 +01002001 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002002 }
2003
2004 toggle_io_reset();
2005
2006 printram("CPE\n");
2007 precharge(ctrl);
2008 printram("CPF\n");
2009
2010 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002011 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002012 }
2013
2014 FOR_ALL_POPULATED_CHANNELS {
2015 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01002016 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002017 }
2018
2019 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2020 err = discover_timC(ctrl, channel, slotrank);
2021 if (err)
2022 return err;
2023 }
2024
2025 FOR_ALL_POPULATED_CHANNELS
2026 program_timings(ctrl, channel);
2027
2028 /* measure and adjust timB timings */
2029 adjust_high_timB(ctrl);
2030
2031 FOR_ALL_POPULATED_CHANNELS
2032 program_timings(ctrl, channel);
2033
2034 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002035 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002036 }
2037 return 0;
2038}
2039
Angel Pons88521882020-01-05 20:21:20 +01002040static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002041{
2042 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2043 int timC_delta;
2044 int lanes_ok = 0;
2045 int ctr = 0;
2046 int lane;
2047
2048 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2049 FOR_ALL_LANES {
2050 ctrl->timings[channel][slotrank].lanes[lane].timC =
2051 saved_rt.lanes[lane].timC + timC_delta;
2052 }
2053 program_timings(ctrl, channel);
2054 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002055 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002056 }
2057
Angel Pons88521882020-01-05 20:21:20 +01002058 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002059
Angel Pons88521882020-01-05 20:21:20 +01002060 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002061 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002062 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002063 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002064 8, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
2065 ctr, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002066 0, 0, 1, 0, 18, 0, 0, 0);
Felix Held9fe248f2018-07-31 20:59:45 +02002067
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002068 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002069 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002070 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002071 32, 4, ctrl->CWL + ctrl->tWTR + 8, SSQ_WR,
2072 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002073 0, 1, 0, 0, 18, 3, 0, 2);
Angel Ponsca00dec2020-05-02 15:04:00 +02002074
Angel Ponsc36cd072020-05-02 16:51:39 +02002075 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002076
2077 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002078 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002079 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002080 32, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
2081 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002082 0, 1, 0, 0, 18, 3, 0, 2);
Angel Ponsca00dec2020-05-02 15:04:00 +02002083
Angel Ponsc36cd072020-05-02 16:51:39 +02002084 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002085
2086 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002087 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002088 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002089 1, 4, 15, SSQ_NA,
2090 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002091 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002092
Angel Pons7c49cb82020-03-16 23:17:32 +01002093 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002094 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002095
Angel Pons88521882020-01-05 20:21:20 +01002096 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002097 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002098 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002099
2100 if (r32 == 0)
2101 lanes_ok |= 1 << lane;
2102 }
2103 ctr++;
Patrick Rudolphdd662872017-10-28 18:20:11 +02002104 if (lanes_ok == ((1 << ctrl->lanes) - 1))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002105 break;
2106 }
2107
2108 ctrl->timings[channel][slotrank] = saved_rt;
2109
Patrick Rudolphdd662872017-10-28 18:20:11 +02002110 return lanes_ok != ((1 << ctrl->lanes) - 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002111}
2112
Angel Pons88521882020-01-05 20:21:20 +01002113static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002114{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302115 unsigned int i, j;
Angel Pons7c49cb82020-03-16 23:17:32 +01002116 unsigned int offset = get_precedening_channels(ctrl, channel) * 0x40;
2117 unsigned int step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002118
2119 if (patno) {
2120 u8 base8 = 0x80 >> ((patno - 1) % 8);
2121 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2122 for (i = 0; i < 32; i++) {
2123 for (j = 0; j < 16; j++) {
2124 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01002125
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002126 if (invert[patno - 1][i] & (1 << (j / 2)))
2127 val = ~val;
Angel Pons7c49cb82020-03-16 23:17:32 +01002128
2129 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002130 }
2131 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002132 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002133 for (i = 0; i < ARRAY_SIZE(pattern); i++) {
2134 for (j = 0; j < 16; j++) {
2135 const u32 val = pattern[i][j];
2136 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
2137 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002138 }
2139 sfence();
2140 }
2141}
2142
Angel Pons88521882020-01-05 20:21:20 +01002143static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002144{
2145 int channel, slotrank;
2146
2147 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002148 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002149
Angel Pons7c49cb82020-03-16 23:17:32 +01002150 /* Choose an existing rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002151 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2152
2153 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02002154 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002155 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02002156 1, 4, 4, SSQ_NA,
2157 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002158 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002159
Angel Pons7c49cb82020-03-16 23:17:32 +01002160 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002161 iosav_run_once(channel, 1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002162
Angel Pons88521882020-01-05 20:21:20 +01002163 wait_for_iosav(channel);
2164 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002165 }
2166
2167 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002168 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002169 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002170 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002171
2172 /* choose an existing rank. */
2173 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2174
2175 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02002176 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002177 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02002178 1, 4, 4, SSQ_NA,
2179 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002180 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002181
Angel Pons7c49cb82020-03-16 23:17:32 +01002182 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002183 iosav_run_once(channel, 1);
Felix Held9cf1dd22018-07-31 14:52:40 +02002184
Angel Pons88521882020-01-05 20:21:20 +01002185 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002186 }
2187
Angel Pons7c49cb82020-03-16 23:17:32 +01002188 /* JEDEC reset */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002189 dram_jedecreset(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01002190
2191 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002192 dram_mrscommands(ctrl);
2193
2194 toggle_io_reset();
2195}
2196
2197#define MIN_C320C_LEN 13
2198
2199static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2200{
2201 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2202 int slotrank;
2203 int c320c;
2204 int stat[NUM_SLOTRANKS][256];
2205 int delta = 0;
2206
2207 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2208
2209 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002210 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002211 }
2212
2213 ctrl->cmd_stretch[channel] = cmd_stretch;
2214
Angel Pons88521882020-01-05 20:21:20 +01002215 MCHBAR32(TC_RAP_ch(channel)) =
Angel Pons7c49cb82020-03-16 23:17:32 +01002216 (ctrl->tRRD << 0)
2217 | (ctrl->tRTP << 4)
2218 | (ctrl->tCKE << 8)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002219 | (ctrl->tWTR << 12)
2220 | (ctrl->tFAW << 16)
Angel Pons7c49cb82020-03-16 23:17:32 +01002221 | (ctrl->tWR << 24)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002222 | (ctrl->cmd_stretch[channel] << 30);
2223
2224 if (ctrl->cmd_stretch[channel] == 2)
2225 delta = 2;
2226 else if (ctrl->cmd_stretch[channel] == 0)
2227 delta = 4;
2228
2229 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002230 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002231 }
2232
2233 for (c320c = -127; c320c <= 127; c320c++) {
2234 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002235 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002236 }
2237 program_timings(ctrl, channel);
2238 reprogram_320c(ctrl);
2239 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002240 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002241 }
2242 }
2243 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002244 struct run rn = get_longest_zero_run(stat[slotrank], 255);
2245
Angel Pons88521882020-01-05 20:21:20 +01002246 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002247 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2248 channel, slotrank, rn.start, rn.middle, rn.end);
Angel Pons7c49cb82020-03-16 23:17:32 +01002249
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002250 if (rn.all || rn.length < MIN_C320C_LEN) {
2251 FOR_ALL_POPULATED_RANKS {
2252 ctrl->timings[channel][slotrank] =
2253 saved_timings[channel][slotrank];
2254 }
2255 return MAKE_ERR;
2256 }
2257 }
2258
2259 return 0;
2260}
2261
Angel Pons7c49cb82020-03-16 23:17:32 +01002262/*
2263 * Adjust CMD phase shift and try multiple command rates.
2264 * A command rate of 2T doubles the time needed for address and command decode.
2265 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002266int command_training(ramctr_timing *ctrl)
2267{
2268 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002269
2270 FOR_ALL_POPULATED_CHANNELS {
2271 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002272 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002273 }
2274
2275 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002276 int cmdrate, err;
2277
2278 /*
2279 * Dual DIMM per channel:
Angel Pons7c49cb82020-03-16 23:17:32 +01002280 * Issue:
2281 * While c320c discovery seems to succeed raminit will fail in write training.
2282 *
2283 * Workaround:
2284 * Skip 1T in dual DIMM mode, that's only supported by a few DIMMs.
2285 * Only try 1T mode for XMP DIMMs that request it in dual DIMM mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002286 *
2287 * Single DIMM per channel:
2288 * Try command rate 1T and 2T
2289 */
2290 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002291 if (ctrl->tCMD)
2292 /* XMP gives the CMD rate in clock ticks, not ns */
2293 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002294
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002295 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002296 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2297
2298 if (!err)
2299 break;
2300 }
2301
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002302 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002303 printk(BIOS_EMERG, "c320c discovery failed\n");
2304 return err;
2305 }
2306
Angel Pons891f2bc2020-01-10 01:27:28 +01002307 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002308 }
2309
2310 FOR_ALL_POPULATED_CHANNELS
2311 program_timings(ctrl, channel);
2312
2313 reprogram_320c(ctrl);
2314 return 0;
2315}
2316
Angel Pons891f2bc2020-01-10 01:27:28 +01002317static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002318{
2319 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002320 int stats[NUM_LANES][MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002321 int lane;
2322
2323 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2324 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002325 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
Angel Pons891f2bc2020-01-10 01:27:28 +01002326 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002327 }
2328 program_timings(ctrl, channel);
2329
2330 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002331 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2332 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002333 }
2334
Angel Pons88521882020-01-05 20:21:20 +01002335 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002336
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002337 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002338 write MR3 MPR enable
2339 in this mode only RD and RDA are allowed
2340 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002341 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002342 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002343 1, 3, ctrl->tMOD, SSQ_NA,
2344 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002345 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002346
2347 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002348 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002349 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002350 500, 4, 4, SSQ_RD,
2351 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002352 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002353
2354 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002355 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002356 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002357 1, 4, ctrl->CAS + 8, SSQ_NA,
2358 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002359 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002360
2361 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002362 MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002363 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002364 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002365 1, 3, ctrl->tMOD, SSQ_NA,
2366 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002367 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002368
Angel Pons7c49cb82020-03-16 23:17:32 +01002369 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002370 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002371
Angel Pons88521882020-01-05 20:21:20 +01002372 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002373
2374 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002375 stats[lane][edge] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002376 }
2377 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002378
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002379 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002380 struct run rn = get_longest_zero_run(stats[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002381 edges[lane] = rn.middle;
Angel Pons7c49cb82020-03-16 23:17:32 +01002382
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002383 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002384 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n", channel,
2385 slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002386 return MAKE_ERR;
2387 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002388 printram("eval %d, %d, %d: %02x\n", channel, slotrank, lane, edges[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002389 }
2390 return 0;
2391}
2392
2393int discover_edges(ramctr_timing *ctrl)
2394{
2395 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2396 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2397 int channel, slotrank, lane;
2398 int err;
2399
Angel Pons88521882020-01-05 20:21:20 +01002400 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002401
2402 toggle_io_reset();
2403
2404 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002405 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002406 }
2407
2408 FOR_ALL_POPULATED_CHANNELS {
2409 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002410 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002411 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002412 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002413 }
2414
2415 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002416 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2417 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002418 }
2419
2420 program_timings(ctrl, channel);
2421
2422 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002423 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002424
2425 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002426 MR3 enable MPR
2427 write MR3 MPR enable
2428 in this mode only RD and RDA are allowed
2429 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002430 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002431 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002432 1, 3, ctrl->tMOD, SSQ_NA,
2433 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002434 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002435
2436 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002437 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002438 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002439 3, 4, 4, SSQ_RD,
2440 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002441 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002442
2443 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002444 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002445 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002446 1, 4, ctrl->CAS + 8, SSQ_NA,
2447 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002448 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002449
2450 /* DRAM command MRS
2451 * MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002452 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002453 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002454 1, 3, ctrl->tMOD, SSQ_NA,
2455 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002456 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002457
Angel Pons7c49cb82020-03-16 23:17:32 +01002458 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002459 iosav_run_once(channel, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002460
Angel Pons88521882020-01-05 20:21:20 +01002461 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002462 }
2463
2464 /* XXX: check any measured value ? */
2465
2466 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002467 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
Angel Pons7c49cb82020-03-16 23:17:32 +01002468 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002469 }
2470
2471 program_timings(ctrl, channel);
2472
2473 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002474 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002475
2476 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002477 MR3 enable MPR
2478 write MR3 MPR enable
2479 in this mode only RD and RDA are allowed
2480 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002481 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002482 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002483 1, 3, ctrl->tMOD, SSQ_NA,
2484 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002485 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002486
2487 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002488 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002489 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002490 3, 4, 4, SSQ_RD,
2491 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002492 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002493
2494 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002495 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002496 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002497 1, 4, ctrl->CAS + 8, SSQ_NA,
2498 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002499 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002500
2501 /* DRAM command MRS
2502 * MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002503 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002504 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002505 1, 3, ctrl->tMOD, SSQ_NA,
2506 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002507 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002508
Angel Pons7c49cb82020-03-16 23:17:32 +01002509 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002510 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002511
Angel Pons88521882020-01-05 20:21:20 +01002512 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002513 }
2514
2515 /* XXX: check any measured value ? */
2516
2517 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002518 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002519 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002520 }
2521
2522 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002523 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002524 }
2525
Angel Pons0c3936e2020-03-22 12:49:27 +01002526 /*
2527 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2528 * also use a single loop. It would seem that it is a debugging configuration.
2529 */
Angel Pons88521882020-01-05 20:21:20 +01002530 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2531 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002532
2533 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2534 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002535 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002536 if (err)
2537 return err;
2538 }
2539
Angel Pons88521882020-01-05 20:21:20 +01002540 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2541 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002542
2543 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2544 err = discover_edges_real(ctrl, channel, slotrank,
2545 rising_edges[channel][slotrank]);
2546 if (err)
2547 return err;
2548 }
2549
Angel Pons88521882020-01-05 20:21:20 +01002550 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002551
2552 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2553 ctrl->timings[channel][slotrank].lanes[lane].falling =
2554 falling_edges[channel][slotrank][lane];
2555 ctrl->timings[channel][slotrank].lanes[lane].rising =
2556 rising_edges[channel][slotrank][lane];
2557 }
2558
2559 FOR_ALL_POPULATED_CHANNELS {
2560 program_timings(ctrl, channel);
2561 }
2562
2563 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002564 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002565 }
2566 return 0;
2567}
2568
Angel Pons7c49cb82020-03-16 23:17:32 +01002569static int discover_edges_write_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002570{
2571 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002572 u32 raw_stats[MAX_EDGE_TIMING + 1];
2573 int stats[MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002574 const int reg3000b24[] = { 0, 0xc, 0x2c };
2575 int lane, i;
2576 int lower[NUM_LANES];
2577 int upper[NUM_LANES];
2578 int pat;
2579
2580 FOR_ALL_LANES {
2581 lower[lane] = 0;
2582 upper[lane] = MAX_EDGE_TIMING;
2583 }
2584
2585 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002586 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002587 printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
2588
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002589 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2590 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002591 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002592 printram("using pattern %d\n", pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002593
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002594 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2595 FOR_ALL_LANES {
2596 ctrl->timings[channel][slotrank].lanes[lane].
2597 rising = edge;
2598 ctrl->timings[channel][slotrank].lanes[lane].
2599 falling = edge;
2600 }
2601 program_timings(ctrl, channel);
2602
2603 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002604 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2605 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002606 }
Angel Pons88521882020-01-05 20:21:20 +01002607 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002608
2609 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002610 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002611 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002612 4, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
2613 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002614 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002615
2616 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002617 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002618 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002619 32, 20, ctrl->tWTR + ctrl->CWL + 8, SSQ_WR,
2620 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002621 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002622
2623 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002624 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002625 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002626 32, 20, MAX(ctrl->tRTP, 8), SSQ_RD,
2627 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002628 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002629
2630 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002631 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002632 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002633 1, 3, ctrl->tRP, SSQ_NA,
2634 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002635 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002636
Angel Pons7c49cb82020-03-16 23:17:32 +01002637 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002638 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002639
Angel Pons88521882020-01-05 20:21:20 +01002640 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002641 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002642 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002643 }
2644
Angel Pons7c49cb82020-03-16 23:17:32 +01002645 /* FIXME: This register only exists on Ivy Bridge */
Angel Pons098240eb2020-03-22 12:55:32 +01002646 raw_stats[edge] = MCHBAR32(IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002647 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002648
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002649 FOR_ALL_LANES {
2650 struct run rn;
2651 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
Angel Pons7c49cb82020-03-16 23:17:32 +01002652 stats[edge] = !!(raw_stats[edge] & (1 << lane));
2653
2654 rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
2655
2656 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, "
2657 "0x%02x-0x%02x\n", channel, slotrank, i, rn.start,
2658 rn.middle, rn.end, rn.start + ctrl->edge_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002659 rn.end - ctrl->edge_offset[i]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002660
2661 lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
2662 upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
2663
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002664 edges[lane] = (lower[lane] + upper[lane]) / 2;
2665 if (rn.all || (lower[lane] > upper[lane])) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002666 printk(BIOS_EMERG, "edge write discovery failed: "
2667 "%d, %d, %d\n", channel, slotrank, lane);
2668
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002669 return MAKE_ERR;
2670 }
2671 }
2672 }
2673 }
2674
Angel Pons88521882020-01-05 20:21:20 +01002675 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002676 printram("CPA\n");
2677 return 0;
2678}
2679
2680int discover_edges_write(ramctr_timing *ctrl)
2681{
2682 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
Angel Pons7c49cb82020-03-16 23:17:32 +01002683 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2684 int channel, slotrank, lane, err;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002685
Angel Pons7c49cb82020-03-16 23:17:32 +01002686 /*
2687 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2688 * also use a single loop. It would seem that it is a debugging configuration.
2689 */
Angel Pons88521882020-01-05 20:21:20 +01002690 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2691 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002692
2693 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2694 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002695 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002696 if (err)
2697 return err;
2698 }
2699
Angel Pons88521882020-01-05 20:21:20 +01002700 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2701 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002702
2703 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2704 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002705 rising_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002706 if (err)
2707 return err;
2708 }
2709
Angel Pons88521882020-01-05 20:21:20 +01002710 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002711
2712 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2713 ctrl->timings[channel][slotrank].lanes[lane].falling =
Angel Pons7c49cb82020-03-16 23:17:32 +01002714 falling_edges[channel][slotrank][lane];
2715
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002716 ctrl->timings[channel][slotrank].lanes[lane].rising =
Angel Pons7c49cb82020-03-16 23:17:32 +01002717 rising_edges[channel][slotrank][lane];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002718 }
2719
2720 FOR_ALL_POPULATED_CHANNELS
2721 program_timings(ctrl, channel);
2722
2723 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002724 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002725 }
2726 return 0;
2727}
2728
2729static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2730{
Angel Pons88521882020-01-05 20:21:20 +01002731 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002732
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002733 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002734 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002735 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002736 4, MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD), ctrl->tRCD, SSQ_NA,
2737 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002738 0, 0, 1, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002739
2740 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002741 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002742 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002743 480, 4, ctrl->tWTR + ctrl->CWL + 8, SSQ_WR,
2744 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002745 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002746
2747 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002748 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002749 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002750 480, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
2751 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002752 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002753
2754 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002755 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002756 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002757 1, 4, ctrl->tRP, SSQ_NA,
2758 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002759 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002760
Angel Pons7c49cb82020-03-16 23:17:32 +01002761 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002762 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002763
Angel Pons88521882020-01-05 20:21:20 +01002764 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002765}
2766
2767int discover_timC_write(ramctr_timing *ctrl)
2768{
Angel Pons7c49cb82020-03-16 23:17:32 +01002769 const u8 rege3c_b24[3] = { 0, 0x0f, 0x2f };
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002770 int i, pat;
2771
2772 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2773 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2774 int channel, slotrank, lane;
2775
2776 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2777 lower[channel][slotrank][lane] = 0;
2778 upper[channel][slotrank][lane] = MAX_TIMC;
2779 }
2780
Angel Pons88521882020-01-05 20:21:20 +01002781 /*
2782 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2783 * FIXME: This must only be done on Ivy Bridge.
2784 */
2785 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002786 printram("discover timC write:\n");
2787
2788 for (i = 0; i < 3; i++)
2789 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002790
2791 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
2792 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel),
2793 ~0x3f000000, rege3c_b24[i] << 24);
2794
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002795 udelay(2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002796
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002797 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2798 FOR_ALL_POPULATED_RANKS {
2799 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01002800 u32 raw_stats[MAX_TIMC + 1];
2801 int stats[MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002802
2803 /* Make sure rn.start < rn.end */
Angel Pons7c49cb82020-03-16 23:17:32 +01002804 stats[MAX_TIMC] = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002805
2806 fill_pattern5(ctrl, channel, pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002807 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
2808
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002809 for (timC = 0; timC < MAX_TIMC; timC++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002810 FOR_ALL_LANES {
2811 ctrl->timings[channel][slotrank]
2812 .lanes[lane].timC = timC;
2813 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002814 program_timings(ctrl, channel);
2815
2816 test_timC_write (ctrl, channel, slotrank);
2817
Angel Pons7c49cb82020-03-16 23:17:32 +01002818 /* FIXME: Another IVB-only register! */
Angel Pons098240eb2020-03-22 12:55:32 +01002819 raw_stats[timC] = MCHBAR32(
2820 IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002821 }
2822 FOR_ALL_LANES {
2823 struct run rn;
Angel Pons7c49cb82020-03-16 23:17:32 +01002824 for (timC = 0; timC < MAX_TIMC; timC++) {
2825 stats[timC] = !!(raw_stats[timC]
2826 & (1 << lane));
2827 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002828
Angel Pons7c49cb82020-03-16 23:17:32 +01002829 rn = get_longest_zero_run(stats, MAX_TIMC + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002830 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002831 printk(BIOS_EMERG,
2832 "timC write discovery failed: "
2833 "%d, %d, %d\n", channel,
2834 slotrank, lane);
2835
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002836 return MAKE_ERR;
2837 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002838 printram("timC: %d, %d, %d: "
2839 "0x%02x-0x%02x-0x%02x, "
2840 "0x%02x-0x%02x\n", channel, slotrank,
2841 i, rn.start, rn.middle, rn.end,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002842 rn.start + ctrl->timC_offset[i],
Angel Pons7c49cb82020-03-16 23:17:32 +01002843 rn.end - ctrl->timC_offset[i]);
2844
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002845 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002846 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002847 lower[channel][slotrank][lane]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002848
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002849 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002850 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002851 upper[channel][slotrank][lane]);
2852
2853 }
2854 }
2855 }
2856 }
2857
2858 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002859 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
Angel Pons88521882020-01-05 20:21:20 +01002860 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002861 udelay(2);
2862 }
2863
Angel Pons88521882020-01-05 20:21:20 +01002864 /*
2865 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2866 * FIXME: This must only be done on Ivy Bridge.
2867 */
2868 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002869
2870 printram("CPB\n");
2871
2872 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002873 printram("timC %d, %d, %d: %x\n", channel, slotrank, lane,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002874 (lower[channel][slotrank][lane] +
2875 upper[channel][slotrank][lane]) / 2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002876
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002877 ctrl->timings[channel][slotrank].lanes[lane].timC =
2878 (lower[channel][slotrank][lane] +
2879 upper[channel][slotrank][lane]) / 2;
2880 }
2881 FOR_ALL_POPULATED_CHANNELS {
2882 program_timings(ctrl, channel);
2883 }
2884 return 0;
2885}
2886
Angel Pons88521882020-01-05 20:21:20 +01002887void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002888{
2889 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002890 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002891
2892 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2893 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002894 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002895 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002896 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01002897 printram("normalize %d, %d, %d: mat %d\n",
2898 channel, slotrank, lane, mat);
2899
Felix Heldef4fe3e2019-12-31 14:15:05 +01002900 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01002901 printram("normalize %d, %d, %d: delta %d\n",
2902 channel, slotrank, lane, delta);
2903
Angel Pons88521882020-01-05 20:21:20 +01002904 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01002905 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002906 }
2907
2908 FOR_ALL_POPULATED_CHANNELS {
2909 program_timings(ctrl, channel);
2910 }
2911}
2912
Angel Pons88521882020-01-05 20:21:20 +01002913void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002914{
2915 int channel, slotrank;
2916
2917 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002918 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002919 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01002920 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002921 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002922 }
2923}
2924
2925int channel_test(ramctr_timing *ctrl)
2926{
2927 int channel, slotrank, lane;
2928
2929 slotrank = 0;
2930 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002931 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01002932 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002933 return MAKE_ERR;
2934 }
2935 FOR_ALL_POPULATED_CHANNELS {
2936 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
2937
Angel Pons88521882020-01-05 20:21:20 +01002938 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002939 }
2940
2941 for (slotrank = 0; slotrank < 4; slotrank++)
2942 FOR_ALL_CHANNELS
2943 if (ctrl->rankmap[channel] & (1 << slotrank)) {
2944 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002945 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
2946 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002947 }
Angel Pons88521882020-01-05 20:21:20 +01002948 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002949
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002950 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002951 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002952 IOSAV_ACT, 1,
Angel Pons2be59002020-05-02 22:15:03 +02002953 4, 8, 40, SSQ_NA,
Angel Ponsca00dec2020-05-02 15:04:00 +02002954 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002955 0, 0, 1, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002956
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002957 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002958 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002959 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002960 100, 4, 40, SSQ_WR,
2961 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002962 0, 1, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002963
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002964 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002965 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002966 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002967 100, 4, 40, SSQ_RD,
2968 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002969 0, 1, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002970
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002971 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002972 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002973 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002974 1, 3, 40, SSQ_NA,
2975 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002976 0, 0, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002977
Angel Pons7c49cb82020-03-16 23:17:32 +01002978 /* Execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02002979 iosav_run_once(channel, 4);
Felix Held9cf1dd22018-07-31 14:52:40 +02002980
Angel Pons88521882020-01-05 20:21:20 +01002981 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002982 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01002983 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002984 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
2985 channel, slotrank, lane);
2986 return MAKE_ERR;
2987 }
2988 }
2989 return 0;
2990}
2991
Patrick Rudolphdd662872017-10-28 18:20:11 +02002992void channel_scrub(ramctr_timing *ctrl)
2993{
2994 int channel, slotrank, row, rowsize;
2995
2996 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
2997 rowsize = 1 << ctrl->info.dimm[channel][slotrank >> 1].row_bits;
2998 for (row = 0; row < rowsize; row += 16) {
2999
3000 wait_for_iosav(channel);
3001
3002 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02003003 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02003004 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02003005 1, MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD), ctrl->tRCD, SSQ_NA,
3006 row, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02003007 1, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003008
3009 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02003010 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02003011 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02003012 129, 4, 40, SSQ_WR,
3013 row, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02003014 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003015
3016 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02003017 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02003018 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02003019 1, 3, 40, SSQ_NA,
3020 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02003021 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003022
3023 /* execute command queue */
Angel Ponsad704002020-05-02 22:51:58 +02003024 iosav_run_once(channel, 3);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003025
3026 wait_for_iosav(channel);
3027 }
3028 }
3029}
3030
Angel Pons88521882020-01-05 20:21:20 +01003031void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003032{
3033 int channel;
3034
Angel Pons7c49cb82020-03-16 23:17:32 +01003035 /* 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 +01003036 static u32 seeds[NUM_CHANNELS][3] = {
3037 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
3038 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
3039 };
3040 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003041 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
Angel Pons7c49cb82020-03-16 23:17:32 +01003042 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
3043 MCHBAR32(SCRAMBLING_SEED_2_HI_ch(channel)) = seeds[channel][1];
3044 MCHBAR32(SCRAMBLING_SEED_2_LO_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003045 }
3046}
3047
Angel Pons89ae6b82020-03-21 13:23:32 +01003048void set_wmm_behavior(const u32 cpu)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003049{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003050 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003051 MCHBAR32(SC_WDBWM) = 0x141d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003052 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01003053 MCHBAR32(SC_WDBWM) = 0x551d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003054 }
3055}
3056
Angel Pons88521882020-01-05 20:21:20 +01003057void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003058{
3059 int channel;
3060
3061 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003062 /* Always drive command bus */
Angel Pons88521882020-01-05 20:21:20 +01003063 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003064 }
3065
3066 udelay(1);
3067
3068 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003069 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003070 }
3071}
3072
Angel Pons7c49cb82020-03-16 23:17:32 +01003073void set_read_write_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003074{
3075 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003076
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003077 FOR_ALL_POPULATED_CHANNELS {
3078 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003079 int min_pi = 10000;
3080 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003081
3082 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003083 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3084 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003085 }
3086
Angel Pons7c49cb82020-03-16 23:17:32 +01003087 b20 = (max_pi - min_pi > 51) ? 0 : ctrl->ref_card_offset[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003088
Angel Pons7c49cb82020-03-16 23:17:32 +01003089 b4_8_12 = (ctrl->pi_coding_threshold < max_pi - min_pi) ? 0x3330 : 0x2220;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003090
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003091 dram_odt_stretch(ctrl, channel);
3092
Angel Pons7c49cb82020-03-16 23:17:32 +01003093 MCHBAR32(TC_RWP_ch(channel)) = 0x0a000000 | (b20 << 20) |
Felix Held2463aa92018-07-29 21:37:55 +02003094 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003095 }
3096}
3097
Angel Pons88521882020-01-05 20:21:20 +01003098void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003099{
3100 int channel;
3101 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003102 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3103 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003104 }
3105}
3106
Angel Pons7c49cb82020-03-16 23:17:32 +01003107/* Encode the watermark latencies in a suitable format for graphics drivers consumption */
3108static int encode_wm(int ns)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003109{
Angel Pons88521882020-01-05 20:21:20 +01003110 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003111}
3112
Angel Pons7c49cb82020-03-16 23:17:32 +01003113/* FIXME: values in this function should be hardware revision-dependent */
Angel Pons88521882020-01-05 20:21:20 +01003114void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003115{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003116 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3117
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003118 int channel;
3119 int t1_cycles = 0, t1_ns = 0, t2_ns;
3120 int t3_ns;
3121 u32 r32;
3122
Angel Pons7c49cb82020-03-16 23:17:32 +01003123 /* FIXME: This register only exists on Ivy Bridge */
3124 MCHBAR32(WMM_READ_CONFIG) = 0x46;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003125
Felix Heldf9b826a2018-07-30 17:56:52 +02003126 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003127 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xffffcfff, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003128
Patrick Rudolph74203de2017-11-20 11:57:01 +01003129 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003130 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003131 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003132 else
Angel Pons7c49cb82020-03-16 23:17:32 +01003133 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003134 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003135
Felix Heldf9b826a2018-07-30 17:56:52 +02003136 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003137 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003138
Angel Pons88521882020-01-05 20:21:20 +01003139 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3140 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003141
3142 FOR_ALL_CHANNELS {
3143 switch (ctrl->rankmap[channel]) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003144 /* Unpopulated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003145 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003146 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003147 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003148 /* Only single-ranked dimms */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003149 case 1:
3150 case 4:
3151 case 5:
Angel Pons7c49cb82020-03-16 23:17:32 +01003152 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x00373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003153 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003154 /* Dual-ranked dimms present */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003155 default:
Angel Pons7c49cb82020-03-16 23:17:32 +01003156 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x009b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003157 break;
3158 }
3159 }
3160
Felix Held50b7ed22019-12-30 20:41:54 +01003161 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
Angel Pons7c49cb82020-03-16 23:17:32 +01003162 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0x00ffffff, 0x00e4d5d0);
Felix Held50b7ed22019-12-30 20:41:54 +01003163 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003164
3165 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003166 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~(3 << 16), 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003167
Angel Pons88521882020-01-05 20:21:20 +01003168 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3169 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3170 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003171
Angel Pons7c49cb82020-03-16 23:17:32 +01003172 /* Find a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003173 FOR_ALL_POPULATED_CHANNELS
3174 break;
3175
Angel Pons88521882020-01-05 20:21:20 +01003176 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3177 r32 = MCHBAR32(PM_DLL_CONFIG);
Angel Pons7c49cb82020-03-16 23:17:32 +01003178 if (r32 & (1 << 17))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003179 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003180 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003181 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
Angel Pons7c49cb82020-03-16 23:17:32 +01003182 if (!(r32 & (1 << 17)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003183 t1_ns += 500;
3184
Angel Pons88521882020-01-05 20:21:20 +01003185 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003186 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003187 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003188 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003189 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003190 t3_ns = 500;
3191 }
Angel Pons7c49cb82020-03-16 23:17:32 +01003192
3193 /* The graphics driver will use these watermark values */
3194 printk(BIOS_DEBUG, "t123: %d, %d, %d\n", t1_ns, t2_ns, t3_ns);
3195 MCHBAR32_AND_OR(SSKPD, 0xC0C0C0C0,
3196 ((encode_wm(t1_ns) + encode_wm(t2_ns)) << 16) | (encode_wm(t1_ns) << 8) |
3197 ((encode_wm(t3_ns) + encode_wm(t2_ns) + encode_wm(t1_ns)) << 24) | 0x0c);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003198}
3199
Angel Pons88521882020-01-05 20:21:20 +01003200void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003201{
3202 int channel, slotrank, lane;
3203
Angel Pons7c49cb82020-03-16 23:17:32 +01003204 FOR_ALL_POPULATED_CHANNELS {
3205 MCHBAR32(TC_RAP_ch(channel)) =
3206 (ctrl->tRRD << 0)
3207 | (ctrl->tRTP << 4)
3208 | (ctrl->tCKE << 8)
3209 | (ctrl->tWTR << 12)
3210 | (ctrl->tFAW << 16)
3211 | (ctrl->tWR << 24)
3212 | (ctrl->cmd_stretch[channel] << 30);
3213 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003214
3215 udelay(1);
3216
3217 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003218 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003219 }
3220
3221 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003222 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003223 }
3224
3225 FOR_ALL_POPULATED_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003226 MCHBAR32_OR(TC_RWP_ch(channel), 0x08000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003227
3228 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003229 udelay(1);
3230 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x00200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003231 }
3232
3233 printram("CPE\n");
3234
Angel Pons88521882020-01-05 20:21:20 +01003235 MCHBAR32(GDCRTRAININGMOD) = 0;
3236 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003237
3238 printram("CP5b\n");
3239
3240 FOR_ALL_POPULATED_CHANNELS {
3241 program_timings(ctrl, channel);
3242 }
3243
3244 u32 reg, addr;
3245
Angel Pons7c49cb82020-03-16 23:17:32 +01003246 /* Poll for RCOMP */
3247 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
3248 ;
3249
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003250 do {
Angel Pons88521882020-01-05 20:21:20 +01003251 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003252 } while ((reg & 0x14) == 0);
3253
Angel Pons7c49cb82020-03-16 23:17:32 +01003254 /* Set state of memory controller */
Angel Pons88521882020-01-05 20:21:20 +01003255 MCHBAR32(MC_INIT_STATE_G) = 0x116;
Angel Pons7c49cb82020-03-16 23:17:32 +01003256 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003257
Angel Pons7c49cb82020-03-16 23:17:32 +01003258 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003259 udelay(500);
3260
3261 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003262 /* Set valid rank CKE */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003263 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01003264 reg = (reg & ~0x0f) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003265 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003266 MCHBAR32(addr) = reg;
3267
Angel Pons7c49cb82020-03-16 23:17:32 +01003268 /* Wait 10ns for ranks to settle */
3269 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003270
3271 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3272 MCHBAR32(addr) = reg;
3273
Angel Pons7c49cb82020-03-16 23:17:32 +01003274 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003275 write_reset(ctrl);
3276 }
3277
Angel Pons7c49cb82020-03-16 23:17:32 +01003278 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003279 dram_mrscommands(ctrl);
3280
3281 printram("CP5c\n");
3282
Angel Pons88521882020-01-05 20:21:20 +01003283 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003284
3285 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003286 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003287 udelay(2);
3288 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003289}