blob: b5d337dc748d46ec9c67c4962ef99b8058dc3cb3 [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
Angel Pons38d901e2020-05-02 23:50:43 +020023static void iosav_write_ssq(const int ch, const struct iosav_ssq *ssq)
Angel Ponsd5b780c2020-05-02 21:48:46 +020024{
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 Pons38d901e2020-05-02 23:50:43 +020033static void iosav_run_queue(const int ch, const u8 loops, const u8 as_timer)
Angel Ponse7afcd532020-05-02 23:14:27 +020034{
Angel Ponsd5b780c2020-05-02 21:48:46 +020035 MCHBAR32(IOSAV_SEQ_CTL_ch(ch)) = loops | ((ssq_count - 1) << 18) | (as_timer << 22);
36
37 ssq_count = 0;
Angel Ponse7afcd532020-05-02 23:14:27 +020038}
Angel Ponsad704002020-05-02 22:51:58 +020039
Angel Pons38d901e2020-05-02 23:50:43 +020040static void iosav_run_once(const int ch)
Angel Ponse7afcd532020-05-02 23:14:27 +020041{
Angel Pons38d901e2020-05-02 23:50:43 +020042 iosav_run_queue(ch, 1, 0);
Angel Ponse7afcd532020-05-02 23:14:27 +020043}
Felix Held9cf1dd22018-07-31 14:52:40 +020044
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010045static void sfence(void)
46{
47 asm volatile ("sfence");
48}
49
Angel Pons7c49cb82020-03-16 23:17:32 +010050/* Toggle IO reset bit */
51static void toggle_io_reset(void)
52{
Angel Pons88521882020-01-05 20:21:20 +010053 u32 r32 = MCHBAR32(MC_INIT_STATE_G);
Angel Pons7c49cb82020-03-16 23:17:32 +010054 MCHBAR32(MC_INIT_STATE_G) = r32 | 0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010055 udelay(1);
Angel Pons88521882020-01-05 20:21:20 +010056 MCHBAR32(MC_INIT_STATE_G) = r32 & ~0x20;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010057 udelay(1);
58}
59
60static u32 get_XOVER_CLK(u8 rankmap)
61{
62 return rankmap << 24;
63}
64
65static u32 get_XOVER_CMD(u8 rankmap)
66{
67 u32 reg;
68
Angel Pons7c49cb82020-03-16 23:17:32 +010069 /* Enable xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010070 reg = 0x4000;
71
Angel Pons7c49cb82020-03-16 23:17:32 +010072 /* Enable xover ctl */
73 if (rankmap & 0x03)
74 reg |= (1 << 17);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010075
Angel Pons7c49cb82020-03-16 23:17:32 +010076 if (rankmap & 0x0c)
77 reg |= (1 << 26);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010078
79 return reg;
80}
81
Angel Pons7c49cb82020-03-16 23:17:32 +010082/* CAS write latency. To be programmed in MR2. See DDR3 SPEC for MR2 documentation. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010083u8 get_CWL(u32 tCK)
84{
Angel Pons7c49cb82020-03-16 23:17:32 +010085 /* Get CWL based on tCK using the following rule */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010086 switch (tCK) {
87 case TCK_1333MHZ:
88 return 12;
Angel Pons7c49cb82020-03-16 23:17:32 +010089
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010090 case TCK_1200MHZ:
91 case TCK_1100MHZ:
92 return 11;
Angel Pons7c49cb82020-03-16 23:17:32 +010093
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010094 case TCK_1066MHZ:
95 case TCK_1000MHZ:
96 return 10;
Angel Pons7c49cb82020-03-16 23:17:32 +010097
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +010098 case TCK_933MHZ:
99 case TCK_900MHZ:
100 return 9;
Angel Pons7c49cb82020-03-16 23:17:32 +0100101
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100102 case TCK_800MHZ:
103 case TCK_700MHZ:
104 return 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100105
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100106 case TCK_666MHZ:
107 return 7;
Angel Pons7c49cb82020-03-16 23:17:32 +0100108
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100109 case TCK_533MHZ:
110 return 6;
Angel Pons7c49cb82020-03-16 23:17:32 +0100111
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100112 default:
113 return 5;
114 }
115}
116
117void dram_find_common_params(ramctr_timing *ctrl)
118{
119 size_t valid_dimms;
120 int channel, slot;
121 dimm_info *dimms = &ctrl->info;
122
123 ctrl->cas_supported = (1 << (MAX_CAS - MIN_CAS + 1)) - 1;
124 valid_dimms = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100125
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100126 FOR_ALL_CHANNELS for (slot = 0; slot < 2; slot++) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100127
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100128 const dimm_attr *dimm = &dimms->dimm[channel][slot];
129 if (dimm->dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3)
130 continue;
Angel Pons7c49cb82020-03-16 23:17:32 +0100131
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100132 valid_dimms++;
133
134 /* Find all possible CAS combinations */
135 ctrl->cas_supported &= dimm->cas_supported;
136
137 /* Find the smallest common latencies supported by all DIMMs */
Angel Pons7c49cb82020-03-16 23:17:32 +0100138 ctrl->tCK = MAX(ctrl->tCK, dimm->tCK);
139 ctrl->tAA = MAX(ctrl->tAA, dimm->tAA);
140 ctrl->tWR = MAX(ctrl->tWR, dimm->tWR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100141 ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD);
142 ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD);
Angel Pons7c49cb82020-03-16 23:17:32 +0100143 ctrl->tRP = MAX(ctrl->tRP, dimm->tRP);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100144 ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS);
145 ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC);
146 ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR);
147 ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP);
148 ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW);
Dan Elkoubydabebc32018-04-13 18:47:10 +0300149 ctrl->tCWL = MAX(ctrl->tCWL, dimm->tCWL);
150 ctrl->tCMD = MAX(ctrl->tCMD, dimm->tCMD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100151 }
152
153 if (!ctrl->cas_supported)
Angel Pons7c49cb82020-03-16 23:17:32 +0100154 die("Unsupported DIMM combination. DIMMS do not support common CAS latency");
155
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100156 if (!valid_dimms)
157 die("No valid DIMMs found");
158}
159
Angel Pons88521882020-01-05 20:21:20 +0100160void dram_xover(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100161{
162 u32 reg;
163 int channel;
164
165 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100166 /* Enable xover clk */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100167 reg = get_XOVER_CLK(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100168 printram("XOVER CLK [%x] = %x\n", GDCRCKPICODE_ch(channel), reg);
169 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100170
Angel Pons7c49cb82020-03-16 23:17:32 +0100171 /* Enable xover ctl & xover cmd */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100172 reg = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons88521882020-01-05 20:21:20 +0100173 printram("XOVER CMD [%x] = %x\n", GDCRCMDPICODING_ch(channel), reg);
174 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100175 }
176}
177
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100178static void dram_odt_stretch(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100179{
Angel Pons89ae6b82020-03-21 13:23:32 +0100180 u32 addr, stretch;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100181
182 stretch = ctrl->ref_card_offset[channel];
Angel Pons7c49cb82020-03-16 23:17:32 +0100183 /*
184 * ODT stretch:
185 * Delay ODT signal by stretch value. Useful for multi DIMM setups on the same channel.
186 */
Angel Pons89ae6b82020-03-21 13:23:32 +0100187 if (IS_SANDY_CPU(ctrl->cpu) && IS_SANDY_CPU_C(ctrl->cpu)) {
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100188 if (stretch == 2)
189 stretch = 3;
Angel Pons7c49cb82020-03-16 23:17:32 +0100190
Angel Pons88521882020-01-05 20:21:20 +0100191 addr = SCHED_SECOND_CBIT_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100192 MCHBAR32_AND_OR(addr, 0xffffc3ff, (stretch << 12) | (stretch << 10));
193 printk(RAM_DEBUG, "OTHP Workaround [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100194 } else {
Angel Pons88521882020-01-05 20:21:20 +0100195 addr = TC_OTHP_ch(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100196 MCHBAR32_AND_OR(addr, 0xfff0ffff, (stretch << 16) | (stretch << 18));
Iru Cai89af71c2018-08-16 16:46:27 +0800197 printk(RAM_DEBUG, "OTHP [%x] = %x\n", addr, MCHBAR32(addr));
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100198 }
199}
200
201void dram_timing_regs(ramctr_timing *ctrl)
202{
203 u32 reg, addr, val32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100204 int channel;
205
206 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100207 /* BIN parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100208 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100209 reg |= (ctrl->tRCD << 0);
210 reg |= (ctrl->tRP << 4);
211 reg |= (ctrl->CAS << 8);
212 reg |= (ctrl->CWL << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100213 reg |= (ctrl->tRAS << 16);
Angel Pons88521882020-01-05 20:21:20 +0100214 printram("DBP [%x] = %x\n", TC_DBP_ch(channel), reg);
215 MCHBAR32(TC_DBP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100216
Angel Pons7c49cb82020-03-16 23:17:32 +0100217 /* Regular access parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100218 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100219 reg |= (ctrl->tRRD << 0);
220 reg |= (ctrl->tRTP << 4);
221 reg |= (ctrl->tCKE << 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100222 reg |= (ctrl->tWTR << 12);
223 reg |= (ctrl->tFAW << 16);
Angel Pons7c49cb82020-03-16 23:17:32 +0100224 reg |= (ctrl->tWR << 24);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100225 reg |= (3 << 30);
Angel Pons88521882020-01-05 20:21:20 +0100226 printram("RAP [%x] = %x\n", TC_RAP_ch(channel), reg);
227 MCHBAR32(TC_RAP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100228
Angel Pons7c49cb82020-03-16 23:17:32 +0100229 /* Other parameters */
Angel Pons88521882020-01-05 20:21:20 +0100230 addr = TC_OTHP_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100231 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100232 reg |= (ctrl->tXPDLL << 0);
233 reg |= (ctrl->tXP << 5);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100234 reg |= (ctrl->tAONPD << 8);
235 reg |= 0xa0000;
236 printram("OTHP [%x] = %x\n", addr, reg);
237 MCHBAR32(addr) = reg;
238
Angel Ponsca2f68a2020-03-22 13:15:12 +0100239 /* Debug parameters - only applies to Ivy Bridge */
240 if (IS_IVY_CPU(ctrl->cpu)) {
241 reg = 0;
242
243 /*
244 * If tXP and tXPDLL are very high, we need to increase them by one.
245 * This can only happen on Ivy Bridge, and when overclocking the RAM.
246 */
247 if (ctrl->tXP >= 8)
248 reg |= (1 << 12);
249
250 if (ctrl->tXPDLL >= 32)
251 reg |= (1 << 13);
252
253 MCHBAR32(TC_DTP_ch(channel)) = reg;
254 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100255
Felix Held9fe248f2018-07-31 20:59:45 +0200256 MCHBAR32_OR(addr, 0x00020000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100257
Patrick Rudolph19c3dad2016-11-26 11:37:45 +0100258 dram_odt_stretch(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100259
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100260 /*
Angel Pons7c49cb82020-03-16 23:17:32 +0100261 * TC-Refresh timing parameters:
262 * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow
263 * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024.
Patrick Rudolph5ee9bc12017-10-31 10:49:52 +0100264 */
265 val32 = MIN((ctrl->tREFI * 89) / 10, (70000 << 8) / ctrl->tCK);
266
Angel Pons7c49cb82020-03-16 23:17:32 +0100267 reg = ((ctrl->tREFI & 0xffff) << 0) |
268 ((ctrl->tRFC & 0x01ff) << 16) | (((val32 / 1024) & 0x7f) << 25);
269
Angel Pons88521882020-01-05 20:21:20 +0100270 printram("REFI [%x] = %x\n", TC_RFTP_ch(channel), reg);
271 MCHBAR32(TC_RFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100272
Angel Pons88521882020-01-05 20:21:20 +0100273 MCHBAR32_OR(TC_RFP_ch(channel), 0xff);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100274
Angel Pons7c49cb82020-03-16 23:17:32 +0100275 /* Self-refresh timing parameters */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100276 reg = 0;
277 val32 = tDLLK;
Angel Pons7c49cb82020-03-16 23:17:32 +0100278 reg = (reg & ~0x00000fff) | (val32 << 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100279 val32 = ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100280 reg = (reg & ~0x0000f000) | (val32 << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100281 val32 = tDLLK - ctrl->tXSOffset;
Angel Pons7c49cb82020-03-16 23:17:32 +0100282 reg = (reg & ~0x03ff0000) | (val32 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100283 val32 = ctrl->tMOD - 8;
Angel Pons7c49cb82020-03-16 23:17:32 +0100284 reg = (reg & ~0xf0000000) | (val32 << 28);
285 printram("SRFTP [%x] = %x\n", TC_SRFTP_ch(channel), reg);
Angel Pons88521882020-01-05 20:21:20 +0100286 MCHBAR32(TC_SRFTP_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100287 }
288}
289
290void dram_dimm_mapping(ramctr_timing *ctrl)
291{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100292 int channel;
293 dimm_info *info = &ctrl->info;
294
295 FOR_ALL_CHANNELS {
Nico Huberac4f2162017-10-01 18:14:43 +0200296 dimm_attr *dimmA, *dimmB;
297 u32 reg = 0;
298
Angel Pons7c49cb82020-03-16 23:17:32 +0100299 if (info->dimm[channel][0].size_mb >= info->dimm[channel][1].size_mb) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100300 dimmA = &info->dimm[channel][0];
301 dimmB = &info->dimm[channel][1];
Angel Pons7c49cb82020-03-16 23:17:32 +0100302 reg |= (0 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100303 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100304 dimmA = &info->dimm[channel][1];
305 dimmB = &info->dimm[channel][0];
Angel Pons7c49cb82020-03-16 23:17:32 +0100306 reg |= (1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100307 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100308
Nico Huberac4f2162017-10-01 18:14:43 +0200309 if (dimmA && (dimmA->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100310 reg |= (dimmA->size_mb / 256) << 0;
311 reg |= (dimmA->ranks - 1) << 17;
Nico Huberac4f2162017-10-01 18:14:43 +0200312 reg |= (dimmA->width / 8 - 1) << 19;
313 }
314
315 if (dimmB && (dimmB->ranks > 0)) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100316 reg |= (dimmB->size_mb / 256) << 8;
317 reg |= (dimmB->ranks - 1) << 18;
Nico Huberac4f2162017-10-01 18:14:43 +0200318 reg |= (dimmB->width / 8 - 1) << 20;
319 }
320
Angel Pons7c49cb82020-03-16 23:17:32 +0100321 reg |= 1 << 21; /* Rank interleave */
322 reg |= 1 << 22; /* Enhanced interleave */
Nico Huberac4f2162017-10-01 18:14:43 +0200323
Angel Pons7c49cb82020-03-16 23:17:32 +0100324 if ((dimmA && (dimmA->ranks > 0)) || (dimmB && (dimmB->ranks > 0))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100325 ctrl->mad_dimm[channel] = reg;
326 } else {
327 ctrl->mad_dimm[channel] = 0;
328 }
329 }
330}
331
Patrick Rudolphdd662872017-10-28 18:20:11 +0200332void dram_dimm_set_mapping(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100333{
334 int channel;
Patrick Rudolphdd662872017-10-28 18:20:11 +0200335 u32 ecc;
336
337 if (ctrl->ecc_enabled)
338 ecc = training ? (1 << 24) : (3 << 24);
339 else
340 ecc = 0;
341
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100342 FOR_ALL_CHANNELS {
Patrick Rudolphdd662872017-10-28 18:20:11 +0200343 MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel] | ecc;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100344 }
Patrick Rudolphdd662872017-10-28 18:20:11 +0200345
346 //udelay(10); /* TODO: Might be needed for ECC configurations; so far works without. */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100347}
348
Angel Pons88521882020-01-05 20:21:20 +0100349void dram_zones(ramctr_timing *ctrl, int training)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100350{
351 u32 reg, ch0size, ch1size;
352 u8 val;
353 reg = 0;
354 val = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100355
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100356 if (training) {
357 ch0size = ctrl->channel_size_mb[0] ? 256 : 0;
358 ch1size = ctrl->channel_size_mb[1] ? 256 : 0;
359 } else {
360 ch0size = ctrl->channel_size_mb[0];
361 ch1size = ctrl->channel_size_mb[1];
362 }
363
364 if (ch0size >= ch1size) {
Angel Pons88521882020-01-05 20:21:20 +0100365 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100366 val = ch1size / 256;
367 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100368 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100369 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100370 MCHBAR32(MAD_CHNL) = 0x24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100371
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100372 } else {
Angel Pons88521882020-01-05 20:21:20 +0100373 reg = MCHBAR32(MAD_ZR);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100374 val = ch0size / 256;
375 reg = (reg & ~0xff000000) | val << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +0100376 reg = (reg & ~0x00ff0000) | (2 * val) << 16;
Angel Pons88521882020-01-05 20:21:20 +0100377 MCHBAR32(MAD_ZR) = reg;
Felix Helddee167e2019-12-30 17:30:16 +0100378 MCHBAR32(MAD_CHNL) = 0x21;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100379 }
380}
381
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100382#define DEFAULT_PCI_MMIO_SIZE 2048
383
384static unsigned int get_mmio_size(void)
385{
386 const struct device *dev;
387 const struct northbridge_intel_sandybridge_config *cfg = NULL;
388
Angel Ponsb31d1d72020-01-10 01:35:09 +0100389 dev = pcidev_path_on_root(PCI_DEVFN(0, 0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100390 if (dev)
391 cfg = dev->chip_info;
392
393 /* If this is zero, it just means devicetree.cb didn't set it */
394 if (!cfg || cfg->pci_mmio_size == 0)
395 return DEFAULT_PCI_MMIO_SIZE;
396 else
397 return cfg->pci_mmio_size;
398}
399
Patrick Rudolph05d4bf7e2017-10-28 16:36:09 +0200400/*
401 * Returns the ECC mode the NB is running at. It takes precedence over ECC capability.
402 * The ME/PCU/.. has the ability to change this.
403 * Return 0: ECC is optional
404 * Return 1: ECC is forced
405 */
406bool get_host_ecc_forced(void)
407{
408 /* read Capabilities A Register */
409 const u32 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
410 return !!(reg32 & (1 << 24));
411}
412
413/*
414 * Returns the ECC capability.
415 * The ME/PCU/.. has the ability to change this.
416 * Return 0: ECC is disabled
417 * Return 1: ECC is possible
418 */
419bool get_host_ecc_cap(void)
420{
421 /* read Capabilities A Register */
422 const u32 reg32 = pci_read_config32(HOST_BRIDGE, CAPID0_A);
423 return !(reg32 & (1 << 25));
424}
425
Angel Pons88521882020-01-05 20:21:20 +0100426void dram_memorymap(ramctr_timing *ctrl, int me_uma_size)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100427{
Angel Pons7c49cb82020-03-16 23:17:32 +0100428 u32 reg, val, reclaim, tom, gfxstolen, gttsize;
429 size_t tsegbase, toludbase, remapbase, gfxstolenbase, mmiosize, gttbase;
430 size_t tsegsize, touudbase, remaplimit, mestolenbase, tsegbasedelta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100431 uint16_t ggc;
432
433 mmiosize = get_mmio_size();
434
Felix Held87ddea22020-01-26 04:55:27 +0100435 ggc = pci_read_config16(HOST_BRIDGE, GGC);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100436 if (!(ggc & 2)) {
437 gfxstolen = ((ggc >> 3) & 0x1f) * 32;
Angel Pons7c49cb82020-03-16 23:17:32 +0100438 gttsize = ((ggc >> 8) & 0x3);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100439 } else {
440 gfxstolen = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100441 gttsize = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100442 }
443
444 tsegsize = CONFIG_SMM_TSEG_SIZE >> 20;
445
446 tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1];
447
448 mestolenbase = tom - me_uma_size;
449
Angel Pons7c49cb82020-03-16 23:17:32 +0100450 toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize, tom - me_uma_size);
451
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100452 gfxstolenbase = toludbase - gfxstolen;
453 gttbase = gfxstolenbase - gttsize;
454
455 tsegbase = gttbase - tsegsize;
456
Angel Pons7c49cb82020-03-16 23:17:32 +0100457 /* Round tsegbase down to nearest address aligned to tsegsize */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100458 tsegbasedelta = tsegbase & (tsegsize - 1);
459 tsegbase &= ~(tsegsize - 1);
460
461 gttbase -= tsegbasedelta;
462 gfxstolenbase -= tsegbasedelta;
463 toludbase -= tsegbasedelta;
464
Angel Pons7c49cb82020-03-16 23:17:32 +0100465 /* Test if it is possible to reclaim a hole in the RAM addressing */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100466 if (tom - me_uma_size > toludbase) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100467 /* Reclaim is possible */
468 reclaim = 1;
469 remapbase = MAX(4096, tom - me_uma_size);
470 remaplimit = remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1;
471 touudbase = remaplimit + 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100472 } else {
473 // Reclaim not possible
Angel Pons7c49cb82020-03-16 23:17:32 +0100474 reclaim = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100475 touudbase = tom - me_uma_size;
476 }
477
Angel Pons7c49cb82020-03-16 23:17:32 +0100478 /* Update memory map in PCIe configuration space */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100479 printk(BIOS_DEBUG, "Update PCI-E configuration space:\n");
480
Angel Pons7c49cb82020-03-16 23:17:32 +0100481 /* TOM (top of memory) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100482 reg = pci_read_config32(HOST_BRIDGE, TOM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100483 val = tom & 0xfff;
484 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100485 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100486 pci_write_config32(HOST_BRIDGE, TOM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100487
Angel Ponsb31d1d72020-01-10 01:35:09 +0100488 reg = pci_read_config32(HOST_BRIDGE, TOM + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100489 val = tom & 0xfffff000;
490 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100491 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOM + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100492 pci_write_config32(HOST_BRIDGE, TOM + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100493
Angel Pons7c49cb82020-03-16 23:17:32 +0100494 /* TOLUD (Top Of Low Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100495 reg = pci_read_config32(HOST_BRIDGE, TOLUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100496 val = toludbase & 0xfff;
497 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100498 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOLUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100499 pci_write_config32(HOST_BRIDGE, TOLUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100500
Angel Pons7c49cb82020-03-16 23:17:32 +0100501 /* TOUUD LSB (Top Of Upper Usable DRAM) */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100502 reg = pci_read_config32(HOST_BRIDGE, TOUUD);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100503 val = touudbase & 0xfff;
504 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100505 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100506 pci_write_config32(HOST_BRIDGE, TOUUD, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100507
Angel Pons7c49cb82020-03-16 23:17:32 +0100508 /* TOUUD MSB */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100509 reg = pci_read_config32(HOST_BRIDGE, TOUUD + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100510 val = touudbase & 0xfffff000;
511 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held4902fee2019-12-28 18:09:47 +0100512 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TOUUD + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100513 pci_write_config32(HOST_BRIDGE, TOUUD + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100514
515 if (reclaim) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100516 /* REMAP BASE */
517 pci_write_config32(HOST_BRIDGE, REMAPBASE, remapbase << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100518 pci_write_config32(HOST_BRIDGE, REMAPBASE + 4, remapbase >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100519
Angel Pons7c49cb82020-03-16 23:17:32 +0100520 /* REMAP LIMIT */
521 pci_write_config32(HOST_BRIDGE, REMAPLIMIT, remaplimit << 20);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100522 pci_write_config32(HOST_BRIDGE, REMAPLIMIT + 4, remaplimit >> 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100523 }
Angel Pons7c49cb82020-03-16 23:17:32 +0100524 /* TSEG */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100525 reg = pci_read_config32(HOST_BRIDGE, TSEGMB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100526 val = tsegbase & 0xfff;
527 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100528 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", TSEGMB, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100529 pci_write_config32(HOST_BRIDGE, TSEGMB, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100530
Angel Pons7c49cb82020-03-16 23:17:32 +0100531 /* GFX stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100532 reg = pci_read_config32(HOST_BRIDGE, BDSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100533 val = gfxstolenbase & 0xfff;
534 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100535 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BDSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100536 pci_write_config32(HOST_BRIDGE, BDSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100537
Angel Pons7c49cb82020-03-16 23:17:32 +0100538 /* GTT stolen memory */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100539 reg = pci_read_config32(HOST_BRIDGE, BGSM);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100540 val = gttbase & 0xfff;
541 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held4902fee2019-12-28 18:09:47 +0100542 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", BGSM, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100543 pci_write_config32(HOST_BRIDGE, BGSM, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100544
545 if (me_uma_size) {
Angel Ponsb31d1d72020-01-10 01:35:09 +0100546 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100547 val = (0x80000 - me_uma_size) & 0xfffff000;
548 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100549 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100550 pci_write_config32(HOST_BRIDGE, MESEG_MASK + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100551
Angel Pons7c49cb82020-03-16 23:17:32 +0100552 /* ME base */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100553 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100554 val = mestolenbase & 0xfff;
555 reg = (reg & ~0xfff00000) | (val << 20);
Felix Held651f99f2019-12-30 16:28:48 +0100556 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100557 pci_write_config32(HOST_BRIDGE, MESEG_BASE, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100558
Angel Ponsb31d1d72020-01-10 01:35:09 +0100559 reg = pci_read_config32(HOST_BRIDGE, MESEG_BASE + 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100560 val = mestolenbase & 0xfffff000;
561 reg = (reg & ~0x000fffff) | (val >> 12);
Felix Held651f99f2019-12-30 16:28:48 +0100562 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_BASE + 4, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100563 pci_write_config32(HOST_BRIDGE, MESEG_BASE + 4, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100564
Angel Pons7c49cb82020-03-16 23:17:32 +0100565 /* ME mask */
Angel Ponsb31d1d72020-01-10 01:35:09 +0100566 reg = pci_read_config32(HOST_BRIDGE, MESEG_MASK);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100567 val = (0x80000 - me_uma_size) & 0xfff;
568 reg = (reg & ~0xfff00000) | (val << 20);
Angel Pons7c49cb82020-03-16 23:17:32 +0100569 reg = reg | ME_STLEN_EN; /* Set ME memory enable */
570 reg = reg | MELCK; /* Set lock bit on ME mem */
Felix Held651f99f2019-12-30 16:28:48 +0100571 printk(BIOS_DEBUG, "PCI(0, 0, 0)[%x] = %x\n", MESEG_MASK, reg);
Angel Ponsb31d1d72020-01-10 01:35:09 +0100572 pci_write_config32(HOST_BRIDGE, MESEG_MASK, reg);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100573 }
574}
575
Angel Pons88521882020-01-05 20:21:20 +0100576static void wait_for_iosav(int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100577{
578 while (1) {
Angel Pons88521882020-01-05 20:21:20 +0100579 if (MCHBAR32(IOSAV_STATUS_ch(channel)) & 0x50)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100580 return;
581 }
582}
583
Angel Pons88521882020-01-05 20:21:20 +0100584static void write_reset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100585{
586 int channel, slotrank;
587
Angel Pons7c49cb82020-03-16 23:17:32 +0100588 /* Choose a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100589 channel = (ctrl->rankmap[0]) ? 0 : 1;
590
Angel Pons88521882020-01-05 20:21:20 +0100591 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100592
Angel Pons7c49cb82020-03-16 23:17:32 +0100593 /* Choose a populated rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100594 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
595
596 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200597 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200598 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200599 1, 3, 8, SSQ_NA,
600 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200601 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100602
Angel Pons7c49cb82020-03-16 23:17:32 +0100603 /*
604 * Execute command queue - why is bit 22 set here?!
605 *
606 * This is actually using the IOSAV state machine as a timer, so refresh is allowed.
607 */
Angel Pons38d901e2020-05-02 23:50:43 +0200608 iosav_run_queue(channel, 1, 1);
Felix Held9cf1dd22018-07-31 14:52:40 +0200609
Angel Pons88521882020-01-05 20:21:20 +0100610 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100611}
612
Angel Pons88521882020-01-05 20:21:20 +0100613void dram_jedecreset(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100614{
Felix Held9fe248f2018-07-31 20:59:45 +0200615 u32 reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100616 int channel;
617
Angel Pons7c49cb82020-03-16 23:17:32 +0100618 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
619 ;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100620 do {
Angel Pons88521882020-01-05 20:21:20 +0100621 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100622 } while ((reg & 0x14) == 0);
623
Angel Pons7c49cb82020-03-16 23:17:32 +0100624 /* Set state of memory controller */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100625 reg = 0x112;
Angel Pons88521882020-01-05 20:21:20 +0100626 MCHBAR32(MC_INIT_STATE_G) = reg;
627 MCHBAR32(MC_INIT_STATE) = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100628 reg |= 2; /* DDR reset */
Angel Pons88521882020-01-05 20:21:20 +0100629 MCHBAR32(MC_INIT_STATE_G) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100630
Angel Pons7c49cb82020-03-16 23:17:32 +0100631 /* Assert DIMM reset signal */
632 MCHBAR32_AND(MC_INIT_STATE_G, ~2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100633
Angel Pons7c49cb82020-03-16 23:17:32 +0100634 /* Wait 200us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100635 udelay(200);
636
Angel Pons7c49cb82020-03-16 23:17:32 +0100637 /* Deassert DIMM reset signal */
Angel Pons88521882020-01-05 20:21:20 +0100638 MCHBAR32_OR(MC_INIT_STATE_G, 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100639
Angel Pons7c49cb82020-03-16 23:17:32 +0100640 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100641 udelay(500);
642
Angel Pons7c49cb82020-03-16 23:17:32 +0100643 /* Enable DCLK */
Angel Pons88521882020-01-05 20:21:20 +0100644 MCHBAR32_OR(MC_INIT_STATE_G, 4);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100645
Angel Pons7c49cb82020-03-16 23:17:32 +0100646 /* XXX Wait 20ns */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100647 udelay(1);
648
649 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100650 /* Set valid rank CKE */
Felix Held9fe248f2018-07-31 20:59:45 +0200651 reg = ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +0100652 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100653
Angel Pons7c49cb82020-03-16 23:17:32 +0100654 /* Wait 10ns for ranks to settle */
655 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100656
657 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
Angel Pons88521882020-01-05 20:21:20 +0100658 MCHBAR32(MC_INIT_STATE_ch(channel)) = reg;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100659
Angel Pons7c49cb82020-03-16 23:17:32 +0100660 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100661 write_reset(ctrl);
662 }
663}
664
665static odtmap get_ODT(ramctr_timing *ctrl, u8 rank, int channel)
666{
Angel Pons7c49cb82020-03-16 23:17:32 +0100667 /* Get ODT based on rankmap */
668 int dimms_per_ch = (ctrl->rankmap[channel] & 1) + ((ctrl->rankmap[channel] >> 2) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100669
670 if (dimms_per_ch == 1) {
Angel Pons7c49cb82020-03-16 23:17:32 +0100671 return (const odtmap){60, 60};
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100672 } else {
673 return (const odtmap){120, 30};
674 }
675}
676
Angel Pons7c49cb82020-03-16 23:17:32 +0100677static void write_mrreg(ramctr_timing *ctrl, int channel, int slotrank, int reg, u32 val)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100678{
Angel Pons88521882020-01-05 20:21:20 +0100679 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100680
681 if (ctrl->rank_mirror[channel][slotrank]) {
682 /* DDR3 Rank1 Address mirror
Angel Pons7c49cb82020-03-16 23:17:32 +0100683 swap the following pins:
684 A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100685 reg = ((reg >> 1) & 1) | ((reg << 1) & 2);
Angel Pons7c49cb82020-03-16 23:17:32 +0100686 val = (val & ~0x1f8) | ((val >> 1) & 0xa8) | ((val & 0xa8) << 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100687 }
688
689 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200690 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200691 IOSAV_MRS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200692 1, 4, 4, SSQ_NA,
693 val, 6, reg, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200694 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100695
696 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200697 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +0200698 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +0200699 1, 4, 4, SSQ_NA,
700 val, 6, reg, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200701 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100702
703 /* DRAM command MRS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200704 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +0200705 IOSAV_MRS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200706 1, 4, ctrl->tMOD, SSQ_NA,
707 val, 6, reg, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200708 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +0200709
Angel Pons7c49cb82020-03-16 23:17:32 +0100710 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +0200711 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100712}
713
Angel Pons88521882020-01-05 20:21:20 +0100714static u32 make_mr0(ramctr_timing *ctrl, u8 rank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100715{
716 u16 mr0reg, mch_cas, mch_wr;
717 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 +0100718 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100719
720 /* DLL Reset - self clearing - set after CLK frequency has been changed */
721 mr0reg = 0x100;
722
Angel Pons7c49cb82020-03-16 23:17:32 +0100723 /* Convert CAS to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100724 if (ctrl->CAS < 12) {
725 mch_cas = (u16) ((ctrl->CAS - 4) << 1);
726 } else {
727 mch_cas = (u16) (ctrl->CAS - 12);
728 mch_cas = ((mch_cas << 1) | 0x1);
729 }
730
Angel Pons7c49cb82020-03-16 23:17:32 +0100731 /* Convert tWR to MCH register friendly */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100732 mch_wr = mch_wr_t[ctrl->tWR - 5];
733
Angel Pons7c49cb82020-03-16 23:17:32 +0100734 mr0reg = (mr0reg & ~0x0004) | ((mch_cas & 0x1) << 2);
735 mr0reg = (mr0reg & ~0x0070) | ((mch_cas & 0xe) << 3);
736 mr0reg = (mr0reg & ~0x0e00) | (mch_wr << 9);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100737
Angel Pons7c49cb82020-03-16 23:17:32 +0100738 /* Precharge PD - Fast (desktop) 1 or slow (mobile) 0 - mostly power-saving feature */
739 mr0reg = (mr0reg & ~(1 << 12)) | (!is_mobile << 12);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100740 return mr0reg;
741}
742
743static void dram_mr0(ramctr_timing *ctrl, u8 rank, int channel)
744{
Felix Held2bb3cdf2018-07-28 00:23:59 +0200745 write_mrreg(ctrl, channel, rank, 0, make_mr0(ctrl, rank));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100746}
747
748static u32 encode_odt(u32 odt)
749{
750 switch (odt) {
751 case 30:
752 return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4
753 case 60:
754 return (1 << 2); // RZQ/4
755 case 120:
756 return (1 << 6); // RZQ/2
757 default:
758 case 0:
759 return 0;
760 }
761}
762
763static u32 make_mr1(ramctr_timing *ctrl, u8 rank, int channel)
764{
765 odtmap odt;
766 u32 mr1reg;
767
768 odt = get_ODT(ctrl, rank, channel);
Angel Pons7c49cb82020-03-16 23:17:32 +0100769 mr1reg = 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100770
771 mr1reg |= encode_odt(odt.rttnom);
772
773 return mr1reg;
774}
775
776static void dram_mr1(ramctr_timing *ctrl, u8 rank, int channel)
777{
778 u16 mr1reg;
779
780 mr1reg = make_mr1(ctrl, rank, channel);
781
782 write_mrreg(ctrl, channel, rank, 1, mr1reg);
783}
784
785static void dram_mr2(ramctr_timing *ctrl, u8 rank, int channel)
786{
787 u16 pasr, cwl, mr2reg;
788 odtmap odt;
789 int srt;
790
791 pasr = 0;
792 cwl = ctrl->CWL - 5;
793 odt = get_ODT(ctrl, rank, channel);
794
795 srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh;
796
797 mr2reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100798 mr2reg = (mr2reg & ~0x07) | pasr;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100799 mr2reg = (mr2reg & ~0x38) | (cwl << 3);
800 mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6);
801 mr2reg = (mr2reg & ~0x80) | (srt << 7);
802 mr2reg |= (odt.rttwr / 60) << 9;
803
804 write_mrreg(ctrl, channel, rank, 2, mr2reg);
805}
806
807static void dram_mr3(ramctr_timing *ctrl, u8 rank, int channel)
808{
809 write_mrreg(ctrl, channel, rank, 3, 0);
810}
811
Angel Pons88521882020-01-05 20:21:20 +0100812void dram_mrscommands(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100813{
814 u8 slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100815 int channel;
816
817 FOR_ALL_POPULATED_CHANNELS {
818 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100819 /* MR2 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100820 dram_mr2(ctrl, slotrank, channel);
821
Angel Pons7c49cb82020-03-16 23:17:32 +0100822 /* MR3 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100823 dram_mr3(ctrl, slotrank, channel);
824
Angel Pons7c49cb82020-03-16 23:17:32 +0100825 /* MR1 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100826 dram_mr1(ctrl, slotrank, channel);
827
Angel Pons7c49cb82020-03-16 23:17:32 +0100828 /* MR0 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100829 dram_mr0(ctrl, slotrank, channel);
830 }
831 }
832
Angel Pons69e17142020-03-23 12:26:29 +0100833 /* DRAM command NOP (without ODT nor chip selects) */
Angel Ponsca00dec2020-05-02 15:04:00 +0200834 IOSAV_SUBSEQUENCE(BROADCAST_CH, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200835 IOSAV_NOP & ~(0xff << 8), 0,
Angel Ponsca00dec2020-05-02 15:04:00 +0200836 1, 4, 15, SSQ_NA,
837 2, 6, 0, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200838 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100839
840 /* DRAM command ZQCL */
Angel Ponsca00dec2020-05-02 15:04:00 +0200841 IOSAV_SUBSEQUENCE(BROADCAST_CH, 1,
Angel Ponsb631d072020-05-02 20:00:32 +0200842 IOSAV_ZQCS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +0200843 1, 4, 400, SSQ_NA,
844 1024, 6, 0, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200845 0, 0, 0, 1, 20, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100846
Angel Pons7c49cb82020-03-16 23:17:32 +0100847 /* Execute command queue on all channels. Do it four times. */
Angel Pons38d901e2020-05-02 23:50:43 +0200848 iosav_run_queue(BROADCAST_CH, 4, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100849
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100850 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100851 /* Wait for ref drained */
Angel Pons88521882020-01-05 20:21:20 +0100852 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100853 }
854
Angel Pons7c49cb82020-03-16 23:17:32 +0100855 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +0100856 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100857
858 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +0100859 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100860
Angel Pons88521882020-01-05 20:21:20 +0100861 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100862
863 slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2;
864
Angel Pons7c49cb82020-03-16 23:17:32 +0100865 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100866 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100867
868 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +0200869 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +0200870 IOSAV_ZQCS, 0,
Angel Pons2be59002020-05-02 22:15:03 +0200871 1, 4, 101, SSQ_NA,
Angel Ponsca00dec2020-05-02 15:04:00 +0200872 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +0200873 0, 0, 0, 0, 31, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +0200874
Angel Pons7c49cb82020-03-16 23:17:32 +0100875 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +0200876 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100877
Angel Pons7c49cb82020-03-16 23:17:32 +0100878 /* Drain */
Angel Pons88521882020-01-05 20:21:20 +0100879 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100880 }
881}
882
Felix Held3b906032020-01-14 17:05:43 +0100883static const u32 lane_base[] = {
884 LANEBASE_B0, LANEBASE_B1, LANEBASE_B2, LANEBASE_B3,
885 LANEBASE_B4, LANEBASE_B5, LANEBASE_B6, LANEBASE_B7,
886 LANEBASE_ECC
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100887};
888
Angel Pons88521882020-01-05 20:21:20 +0100889void program_timings(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100890{
Angel Pons88521882020-01-05 20:21:20 +0100891 u32 reg32, reg_roundtrip_latency, reg_pi_code, reg_logic_delay, reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100892 int lane;
893 int slotrank, slot;
894 int full_shift = 0;
Angel Pons88521882020-01-05 20:21:20 +0100895 u16 pi_coding_ctrl[NUM_SLOTS];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100896
897 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +0100898 if (full_shift < -ctrl->timings[channel][slotrank].pi_coding)
899 full_shift = -ctrl->timings[channel][slotrank].pi_coding;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100900 }
901
902 for (slot = 0; slot < NUM_SLOTS; slot++)
903 switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) {
904 case 0:
905 default:
Angel Pons88521882020-01-05 20:21:20 +0100906 pi_coding_ctrl[slot] = 0x7f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100907 break;
908 case 1:
Angel Pons88521882020-01-05 20:21:20 +0100909 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100910 ctrl->timings[channel][2 * slot + 0].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100911 break;
912 case 2:
Angel Pons88521882020-01-05 20:21:20 +0100913 pi_coding_ctrl[slot] =
Angel Pons7c49cb82020-03-16 23:17:32 +0100914 ctrl->timings[channel][2 * slot + 1].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100915 break;
916 case 3:
Angel Pons88521882020-01-05 20:21:20 +0100917 pi_coding_ctrl[slot] =
918 (ctrl->timings[channel][2 * slot].pi_coding +
Angel Pons7c49cb82020-03-16 23:17:32 +0100919 ctrl->timings[channel][2 * slot + 1].pi_coding) / 2 + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100920 break;
921 }
922
Angel Pons7c49cb82020-03-16 23:17:32 +0100923 /* Enable CMD XOVER */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100924 reg32 = get_XOVER_CMD(ctrl->rankmap[channel]);
Angel Pons7c49cb82020-03-16 23:17:32 +0100925 reg32 |= (pi_coding_ctrl[0] & 0x3f) << 6;
926 reg32 |= (pi_coding_ctrl[0] & 0x40) << 9;
Angel Pons88521882020-01-05 20:21:20 +0100927 reg32 |= (pi_coding_ctrl[1] & 0x7f) << 18;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100928 reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6);
929
Angel Pons88521882020-01-05 20:21:20 +0100930 MCHBAR32(GDCRCMDPICODING_ch(channel)) = reg32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100931
Angel Pons7c49cb82020-03-16 23:17:32 +0100932 /* Enable CLK XOVER */
Angel Pons88521882020-01-05 20:21:20 +0100933 reg_pi_code = get_XOVER_CLK(ctrl->rankmap[channel]);
934 reg_logic_delay = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100935
936 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100937 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Angel Pons88521882020-01-05 20:21:20 +0100938 int offset_pi_code;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100939 if (shift < 0)
940 shift = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100941
Angel Pons88521882020-01-05 20:21:20 +0100942 offset_pi_code = ctrl->pi_code_offset + shift;
Angel Pons7c49cb82020-03-16 23:17:32 +0100943
944 /* Set CLK phase shift */
Angel Pons88521882020-01-05 20:21:20 +0100945 reg_pi_code |= (offset_pi_code & 0x3f) << (6 * slotrank);
946 reg_logic_delay |= ((offset_pi_code >> 6) & 1) << slotrank;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100947 }
948
Angel Pons88521882020-01-05 20:21:20 +0100949 MCHBAR32(GDCRCKPICODE_ch(channel)) = reg_pi_code;
950 MCHBAR32(GDCRCKLOGICDELAY_ch(channel)) = reg_logic_delay;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100951
Angel Pons88521882020-01-05 20:21:20 +0100952 reg_io_latency = MCHBAR32(SC_IO_LATENCY_ch(channel));
Felix Helddee167e2019-12-30 17:30:16 +0100953 reg_io_latency &= 0xffff0000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100954
Angel Pons88521882020-01-05 20:21:20 +0100955 reg_roundtrip_latency = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100956
957 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +0100958 int post_timA_min_high = 7, pre_timA_min_high = 7;
959 int post_timA_max_high = 0, pre_timA_max_high = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100960 int shift_402x = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +0100961 int shift = ctrl->timings[channel][slotrank].pi_coding + full_shift;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100962
963 if (shift < 0)
964 shift = 0;
965
966 FOR_ALL_LANES {
Arthur Heymansabc504f2017-05-15 09:36:44 +0200967 post_timA_min_high = MIN(post_timA_min_high,
968 (ctrl->timings[channel][slotrank].lanes[lane].
969 timA + shift) >> 6);
970 pre_timA_min_high = MIN(pre_timA_min_high,
971 ctrl->timings[channel][slotrank].lanes[lane].
972 timA >> 6);
973 post_timA_max_high = MAX(post_timA_max_high,
974 (ctrl->timings[channel][slotrank].lanes[lane].
975 timA + shift) >> 6);
976 pre_timA_max_high = MAX(pre_timA_max_high,
977 ctrl->timings[channel][slotrank].lanes[lane].
978 timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100979 }
980
981 if (pre_timA_max_high - pre_timA_min_high <
982 post_timA_max_high - post_timA_min_high)
983 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +0100984
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100985 else if (pre_timA_max_high - pre_timA_min_high >
986 post_timA_max_high - post_timA_min_high)
987 shift_402x = -1;
988
Felix Helddee167e2019-12-30 17:30:16 +0100989 reg_io_latency |=
Felix Heldef4fe3e2019-12-31 14:15:05 +0100990 (ctrl->timings[channel][slotrank].io_latency + shift_402x -
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100991 post_timA_min_high) << (4 * slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +0100992
Angel Pons88521882020-01-05 20:21:20 +0100993 reg_roundtrip_latency |=
994 (ctrl->timings[channel][slotrank].roundtrip_latency +
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100995 shift_402x) << (8 * slotrank);
996
997 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +0100998 MCHBAR32(lane_base[lane] + GDCRRX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +0100999 (((ctrl->timings[channel][slotrank].lanes[lane].
1000 timA + shift) & 0x3f)
1001 |
1002 ((ctrl->timings[channel][slotrank].lanes[lane].
1003 rising + shift) << 8)
1004 |
1005 (((ctrl->timings[channel][slotrank].lanes[lane].
1006 timA + shift -
1007 (post_timA_min_high << 6)) & 0x1c0) << 10)
1008 | ((ctrl->timings[channel][slotrank].lanes[lane].
1009 falling + shift) << 20));
1010
Felix Heldfb19c8a2020-01-14 21:27:59 +01001011 MCHBAR32(lane_base[lane] + GDCRTX(channel, slotrank)) =
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001012 (((ctrl->timings[channel][slotrank].lanes[lane].
1013 timC + shift) & 0x3f)
1014 |
1015 (((ctrl->timings[channel][slotrank].lanes[lane].
1016 timB + shift) & 0x3f) << 8)
1017 |
1018 (((ctrl->timings[channel][slotrank].lanes[lane].
1019 timB + shift) & 0x1c0) << 9)
1020 |
1021 (((ctrl->timings[channel][slotrank].lanes[lane].
1022 timC + shift) & 0x40) << 13));
1023 }
1024 }
Angel Pons88521882020-01-05 20:21:20 +01001025 MCHBAR32(SC_ROUNDT_LAT_ch(channel)) = reg_roundtrip_latency;
1026 MCHBAR32(SC_IO_LATENCY_ch(channel)) = reg_io_latency;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001027}
1028
Angel Pons88521882020-01-05 20:21:20 +01001029static void test_timA(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001030{
Angel Pons88521882020-01-05 20:21:20 +01001031 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001032
1033 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001034 write MR3 MPR enable
1035 in this mode only RD and RDA are allowed
1036 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001037 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001038 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001039 1, 3, ctrl->tMOD, SSQ_NA,
1040 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001041 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001042
1043 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001044 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001045 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001046 1, 3, 4, SSQ_RD,
1047 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001048 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001049
1050 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001051 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001052 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001053 15, 4, ctrl->CAS + 36, SSQ_NA,
1054 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001055 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001056
1057 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001058 write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001059 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001060 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001061 1, 3, ctrl->tMOD, SSQ_NA,
1062 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001063 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001064
Angel Pons7c49cb82020-03-16 23:17:32 +01001065 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001066 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001067
Angel Pons88521882020-01-05 20:21:20 +01001068 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001069}
1070
Angel Pons7c49cb82020-03-16 23:17:32 +01001071static int does_lane_work(ramctr_timing *ctrl, int channel, int slotrank, int lane)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001072{
1073 u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA;
Angel Pons7c49cb82020-03-16 23:17:32 +01001074
1075 return (MCHBAR32(lane_base[lane] +
1076 GDCRTRAININGRESULT(channel, (timA / 32) & 1)) >> (timA % 32)) & 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001077}
1078
1079struct run {
1080 int middle;
1081 int end;
1082 int start;
1083 int all;
1084 int length;
1085};
1086
1087static struct run get_longest_zero_run(int *seq, int sz)
1088{
1089 int i, ls;
1090 int bl = 0, bs = 0;
1091 struct run ret;
1092
1093 ls = 0;
1094 for (i = 0; i < 2 * sz; i++)
1095 if (seq[i % sz]) {
1096 if (i - ls > bl) {
1097 bl = i - ls;
1098 bs = ls;
1099 }
1100 ls = i + 1;
1101 }
1102 if (bl == 0) {
1103 ret.middle = sz / 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001104 ret.start = 0;
1105 ret.end = sz;
Jacob Garbere0c181d2019-04-08 22:21:43 -06001106 ret.length = sz;
Angel Pons7c49cb82020-03-16 23:17:32 +01001107 ret.all = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001108 return ret;
1109 }
1110
Angel Pons7c49cb82020-03-16 23:17:32 +01001111 ret.start = bs % sz;
1112 ret.end = (bs + bl - 1) % sz;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001113 ret.middle = (bs + (bl - 1) / 2) % sz;
1114 ret.length = bl;
Angel Pons7c49cb82020-03-16 23:17:32 +01001115 ret.all = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001116
1117 return ret;
1118}
1119
Angel Pons7c49cb82020-03-16 23:17:32 +01001120static void discover_timA_coarse(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001121{
1122 int timA;
1123 int statistics[NUM_LANES][128];
1124 int lane;
1125
1126 for (timA = 0; timA < 128; timA++) {
1127 FOR_ALL_LANES {
1128 ctrl->timings[channel][slotrank].lanes[lane].timA = timA;
1129 }
1130 program_timings(ctrl, channel);
1131
1132 test_timA(ctrl, channel, slotrank);
1133
1134 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001135 statistics[lane][timA] = !does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001136 }
1137 }
1138 FOR_ALL_LANES {
1139 struct run rn = get_longest_zero_run(statistics[lane], 128);
1140 ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle;
1141 upperA[lane] = rn.end;
1142 if (upperA[lane] < rn.middle)
1143 upperA[lane] += 128;
Angel Pons7c49cb82020-03-16 23:17:32 +01001144
Patrick Rudolph368b6152016-11-25 16:36:52 +01001145 printram("timA: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001146 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001147 }
1148}
1149
Angel Pons7c49cb82020-03-16 23:17:32 +01001150static void discover_timA_fine(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001151{
1152 int timA_delta;
1153 int statistics[NUM_LANES][51];
1154 int lane, i;
1155
1156 memset(statistics, 0, sizeof(statistics));
1157
1158 for (timA_delta = -25; timA_delta <= 25; timA_delta++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01001159
1160 FOR_ALL_LANES {
1161 ctrl->timings[channel][slotrank].lanes[lane].timA
1162 = upperA[lane] + timA_delta + 0x40;
1163 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001164 program_timings(ctrl, channel);
1165
1166 for (i = 0; i < 100; i++) {
1167 test_timA(ctrl, channel, slotrank);
1168 FOR_ALL_LANES {
1169 statistics[lane][timA_delta + 25] +=
Angel Pons7c49cb82020-03-16 23:17:32 +01001170 does_lane_work(ctrl, channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001171 }
1172 }
1173 }
1174 FOR_ALL_LANES {
1175 int last_zero, first_all;
1176
1177 for (last_zero = -25; last_zero <= 25; last_zero++)
1178 if (statistics[lane][last_zero + 25])
1179 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01001180
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001181 last_zero--;
1182 for (first_all = -25; first_all <= 25; first_all++)
1183 if (statistics[lane][first_all + 25] == 100)
1184 break;
1185
Angel Pons7c49cb82020-03-16 23:17:32 +01001186 printram("lane %d: %d, %d\n", lane, last_zero, first_all);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001187
1188 ctrl->timings[channel][slotrank].lanes[lane].timA =
Angel Pons7c49cb82020-03-16 23:17:32 +01001189 (last_zero + first_all) / 2 + upperA[lane];
1190
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001191 printram("Aval: %d, %d, %d: %x\n", channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01001192 lane, ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001193 }
1194}
1195
Angel Pons891f2bc2020-01-10 01:27:28 +01001196static int discover_402x(ramctr_timing *ctrl, int channel, int slotrank, int *upperA)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001197{
1198 int works[NUM_LANES];
1199 int lane;
Angel Pons7c49cb82020-03-16 23:17:32 +01001200
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001201 while (1) {
1202 int all_works = 1, some_works = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001203
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001204 program_timings(ctrl, channel);
1205 test_timA(ctrl, channel, slotrank);
Angel Pons7c49cb82020-03-16 23:17:32 +01001206
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001207 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001208 works[lane] = !does_lane_work(ctrl, channel, slotrank, lane);
1209
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001210 if (works[lane])
1211 some_works = 1;
1212 else
1213 all_works = 0;
1214 }
1215 if (all_works)
1216 return 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001217
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001218 if (!some_works) {
Angel Pons88521882020-01-05 20:21:20 +01001219 if (ctrl->timings[channel][slotrank].roundtrip_latency < 2) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001220 printk(BIOS_EMERG, "402x discovery failed (1): %d, %d\n",
1221 channel, slotrank);
1222 return MAKE_ERR;
1223 }
Angel Pons88521882020-01-05 20:21:20 +01001224 ctrl->timings[channel][slotrank].roundtrip_latency -= 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001225 printram("4024 -= 2;\n");
1226 continue;
1227 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001228 ctrl->timings[channel][slotrank].io_latency += 2;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001229 printram("4028 += 2;\n");
Angel Pons7c49cb82020-03-16 23:17:32 +01001230
Felix Heldef4fe3e2019-12-31 14:15:05 +01001231 if (ctrl->timings[channel][slotrank].io_latency >= 0x10) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001232 printk(BIOS_EMERG, "402x discovery failed (2): %d, %d\n",
1233 channel, slotrank);
1234 return MAKE_ERR;
1235 }
1236 FOR_ALL_LANES if (works[lane]) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001237 ctrl->timings[channel][slotrank].lanes[lane].timA += 128;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001238 upperA[lane] += 128;
Angel Pons891f2bc2020-01-10 01:27:28 +01001239 printram("increment %d, %d, %d\n", channel, slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001240 }
1241 }
1242 return 0;
1243}
1244
1245struct timA_minmax {
1246 int timA_min_high, timA_max_high;
1247};
1248
Angel Pons88521882020-01-05 20:21:20 +01001249static void pre_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001250 struct timA_minmax *mnmx)
1251{
1252 int lane;
1253 mnmx->timA_min_high = 7;
1254 mnmx->timA_max_high = 0;
1255
1256 FOR_ALL_LANES {
1257 if (mnmx->timA_min_high >
1258 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1259 mnmx->timA_min_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001260 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001261 if (mnmx->timA_max_high <
1262 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6))
1263 mnmx->timA_max_high =
Angel Pons891f2bc2020-01-10 01:27:28 +01001264 (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001265 }
1266}
1267
Angel Pons88521882020-01-05 20:21:20 +01001268static void post_timA_change(ramctr_timing *ctrl, int channel, int slotrank,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001269 struct timA_minmax *mnmx)
1270{
1271 struct timA_minmax post;
1272 int shift_402x = 0;
1273
Angel Pons7c49cb82020-03-16 23:17:32 +01001274 /* Get changed maxima */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001275 pre_timA_change(ctrl, channel, slotrank, &post);
1276
1277 if (mnmx->timA_max_high - mnmx->timA_min_high <
1278 post.timA_max_high - post.timA_min_high)
1279 shift_402x = +1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001280
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001281 else if (mnmx->timA_max_high - mnmx->timA_min_high >
1282 post.timA_max_high - post.timA_min_high)
1283 shift_402x = -1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001284
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001285 else
1286 shift_402x = 0;
1287
Felix Heldef4fe3e2019-12-31 14:15:05 +01001288 ctrl->timings[channel][slotrank].io_latency += shift_402x;
Angel Pons88521882020-01-05 20:21:20 +01001289 ctrl->timings[channel][slotrank].roundtrip_latency += shift_402x;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001290 printram("4024 += %d;\n", shift_402x);
1291 printram("4028 += %d;\n", shift_402x);
1292}
1293
Angel Pons7c49cb82020-03-16 23:17:32 +01001294/*
1295 * Compensate the skew between DQS and DQs.
1296 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001297 * To ease PCB design, a small skew between Data Strobe signals and Data Signals is allowed.
1298 * The controller has to measure and compensate this skew for every byte-lane. By delaying
Angel Pons7c49cb82020-03-16 23:17:32 +01001299 * either all DQ signals or DQS signal, a full phase shift can be introduced. It is assumed
Angel Pons891f2bc2020-01-10 01:27:28 +01001300 * that one byte-lane's DQs signals have the same routing delay.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001301 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001302 * To measure the actual skew, the DRAM is placed in "read leveling" mode. In read leveling
1303 * mode the DRAM-chip outputs an alternating periodic pattern. The memory controller iterates
1304 * over all possible values to do a full phase shift and issues read commands. With DQS and
Angel Pons7c49cb82020-03-16 23:17:32 +01001305 * DQ in phase the data being read is expected to alternate on every byte:
1306 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001307 * 0xFF 0x00 0xFF ...
Angel Pons7c49cb82020-03-16 23:17:32 +01001308 *
Angel Pons891f2bc2020-01-10 01:27:28 +01001309 * Once the controller has detected this pattern a bit in the result register is set for the
1310 * current phase shift.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001311 */
Angel Pons88521882020-01-05 20:21:20 +01001312int read_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001313{
1314 int channel, slotrank, lane;
1315 int err;
1316
1317 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1318 int all_high, some_high;
1319 int upperA[NUM_LANES];
1320 struct timA_minmax mnmx;
1321
Angel Pons88521882020-01-05 20:21:20 +01001322 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001323
Felix Held2bb3cdf2018-07-28 00:23:59 +02001324 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001325 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001326 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001327 1, 3, ctrl->tRP, SSQ_NA,
1328 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001329 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001330
Angel Pons7c49cb82020-03-16 23:17:32 +01001331 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001332 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001333
Angel Pons88521882020-01-05 20:21:20 +01001334 MCHBAR32(GDCRTRAININGMOD) = (slotrank << 2) | 0x8001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001335
Felix Heldef4fe3e2019-12-31 14:15:05 +01001336 ctrl->timings[channel][slotrank].io_latency = 4;
Angel Pons88521882020-01-05 20:21:20 +01001337 ctrl->timings[channel][slotrank].roundtrip_latency = 55;
Felix Held2bb3cdf2018-07-28 00:23:59 +02001338 program_timings(ctrl, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001339
Felix Held2bb3cdf2018-07-28 00:23:59 +02001340 discover_timA_coarse(ctrl, channel, slotrank, upperA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001341
Felix Held2bb3cdf2018-07-28 00:23:59 +02001342 all_high = 1;
1343 some_high = 0;
1344 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001345 if (ctrl->timings[channel][slotrank].lanes[lane].timA >= 0x40)
Felix Held2bb3cdf2018-07-28 00:23:59 +02001346 some_high = 1;
1347 else
1348 all_high = 0;
1349 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001350
1351 if (all_high) {
Felix Heldef4fe3e2019-12-31 14:15:05 +01001352 ctrl->timings[channel][slotrank].io_latency--;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001353 printram("4028--;\n");
1354 FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001355 ctrl->timings[channel][slotrank].lanes[lane].timA -= 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001356 upperA[lane] -= 0x40;
1357
1358 }
1359 } else if (some_high) {
Angel Pons88521882020-01-05 20:21:20 +01001360 ctrl->timings[channel][slotrank].roundtrip_latency++;
Felix Heldef4fe3e2019-12-31 14:15:05 +01001361 ctrl->timings[channel][slotrank].io_latency++;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001362 printram("4024++;\n");
1363 printram("4028++;\n");
1364 }
1365
1366 program_timings(ctrl, channel);
1367
1368 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1369
1370 err = discover_402x(ctrl, channel, slotrank, upperA);
1371 if (err)
1372 return err;
1373
1374 post_timA_change(ctrl, channel, slotrank, &mnmx);
1375 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1376
1377 discover_timA_fine(ctrl, channel, slotrank, upperA);
1378
1379 post_timA_change(ctrl, channel, slotrank, &mnmx);
1380 pre_timA_change(ctrl, channel, slotrank, &mnmx);
1381
1382 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001383 ctrl->timings[channel][slotrank].lanes[lane].timA -=
1384 mnmx.timA_min_high * 0x40;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001385 }
Felix Heldef4fe3e2019-12-31 14:15:05 +01001386 ctrl->timings[channel][slotrank].io_latency -= mnmx.timA_min_high;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001387 printram("4028 -= %d;\n", mnmx.timA_min_high);
1388
1389 post_timA_change(ctrl, channel, slotrank, &mnmx);
1390
1391 printram("4/8: %d, %d, %x, %x\n", channel, slotrank,
Angel Pons88521882020-01-05 20:21:20 +01001392 ctrl->timings[channel][slotrank].roundtrip_latency,
Felix Heldef4fe3e2019-12-31 14:15:05 +01001393 ctrl->timings[channel][slotrank].io_latency);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001394
1395 printram("final results:\n");
1396 FOR_ALL_LANES
Angel Pons7c49cb82020-03-16 23:17:32 +01001397 printram("Aval: %d, %d, %d: %x\n", channel, slotrank, lane,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001398 ctrl->timings[channel][slotrank].lanes[lane].timA);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001399
Angel Pons88521882020-01-05 20:21:20 +01001400 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001401
1402 toggle_io_reset();
1403 }
1404
1405 FOR_ALL_POPULATED_CHANNELS {
1406 program_timings(ctrl, channel);
1407 }
1408 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001409 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001410 }
1411 return 0;
1412}
1413
Angel Pons88521882020-01-05 20:21:20 +01001414static void test_timC(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001415{
1416 int lane;
1417
1418 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01001419 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
1420 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001421 }
1422
Angel Pons88521882020-01-05 20:21:20 +01001423 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001424
1425 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001426 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001427 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001428 4, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
1429 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001430 0, 0, 1, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001431
1432 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001433 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001434 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001435 1, 4, 4, SSQ_WR,
1436 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001437 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001438
1439 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02001440 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001441 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001442 500, 4, 4, SSQ_WR,
1443 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001444 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001445
1446 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001447 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001448 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001449 1, 3, ctrl->CWL + ctrl->tWTR + 5, SSQ_WR,
1450 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001451 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001452
Angel Pons7c49cb82020-03-16 23:17:32 +01001453 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001454 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001455
Angel Pons88521882020-01-05 20:21:20 +01001456 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001457
1458 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001459 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001460 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001461 1, 3, ctrl->tRP, SSQ_NA,
1462 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001463 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001464
1465 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001466 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001467 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001468 8, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->CAS, SSQ_NA,
1469 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001470 0, 0, 1, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001471
1472 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001473 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001474 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001475 500, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
1476 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001477 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001478
1479 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001480 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001481 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001482 1, 3, ctrl->tRP, SSQ_NA,
1483 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001484 0, 0, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001485
Angel Pons7c49cb82020-03-16 23:17:32 +01001486 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001487 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02001488
Angel Pons88521882020-01-05 20:21:20 +01001489 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001490}
1491
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001492static void timC_threshold_process(int *data, const int count)
1493{
1494 int min = data[0];
1495 int max = min;
1496 int i;
1497 for (i = 1; i < count; i++) {
1498 if (min > data[i])
1499 min = data[i];
Angel Pons7c49cb82020-03-16 23:17:32 +01001500
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001501 if (max < data[i])
1502 max = data[i];
1503 }
Angel Pons7c49cb82020-03-16 23:17:32 +01001504 int threshold = min / 2 + max / 2;
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001505 for (i = 0; i < count; i++)
1506 data[i] = data[i] > threshold;
Angel Pons7c49cb82020-03-16 23:17:32 +01001507
Angel Pons891f2bc2020-01-10 01:27:28 +01001508 printram("threshold=%d min=%d max=%d\n", threshold, min, max);
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001509}
1510
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001511static int discover_timC(ramctr_timing *ctrl, int channel, int slotrank)
1512{
1513 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01001514 int stats[NUM_LANES][MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001515 int lane;
1516
Angel Pons88521882020-01-05 20:21:20 +01001517 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001518
1519 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001520 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001521 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001522 1, 3, ctrl->tRP, SSQ_NA,
1523 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001524 0, 0, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001525
Angel Pons7c49cb82020-03-16 23:17:32 +01001526 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001527 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001528
1529 for (timC = 0; timC <= MAX_TIMC; timC++) {
Angel Pons891f2bc2020-01-10 01:27:28 +01001530 FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane].timC = timC;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001531 program_timings(ctrl, channel);
1532
1533 test_timC(ctrl, channel, slotrank);
1534
1535 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001536 stats[lane][timC] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001537 }
1538 }
1539 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01001540 struct run rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1541
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001542 if (rn.all || rn.length < 8) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001543 printk(BIOS_EMERG, "timC discovery failed: %d, %d, %d\n",
1544 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001545 /*
1546 * With command training not being done yet, the lane can be erroneous.
1547 * Take the average as reference and try again to find a run.
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001548 */
Angel Pons7c49cb82020-03-16 23:17:32 +01001549 timC_threshold_process(stats[lane], ARRAY_SIZE(stats[lane]));
1550 rn = get_longest_zero_run(stats[lane], ARRAY_SIZE(stats[lane]));
1551
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001552 if (rn.all || rn.length < 8) {
1553 printk(BIOS_EMERG, "timC recovery failed\n");
1554 return MAKE_ERR;
1555 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001556 }
Tobias Diedrich87c4f112017-12-07 22:40:20 +01001557 ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle;
Patrick Rudolph368b6152016-11-25 16:36:52 +01001558 printram("timC: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
Felix Held2bb3cdf2018-07-28 00:23:59 +02001559 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001560 }
1561 return 0;
1562}
1563
Angel Pons88521882020-01-05 20:21:20 +01001564static int get_precedening_channels(ramctr_timing *ctrl, int target_channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001565{
1566 int channel, ret = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01001567
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001568 FOR_ALL_POPULATED_CHANNELS if (channel < target_channel)
1569 ret++;
Angel Pons7c49cb82020-03-16 23:17:32 +01001570
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001571 return ret;
1572}
1573
Angel Pons88521882020-01-05 20:21:20 +01001574static void fill_pattern0(ramctr_timing *ctrl, int channel, u32 a, u32 b)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001575{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301576 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001577 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Angel Pons7c49cb82020-03-16 23:17:32 +01001578
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001579 for (j = 0; j < 16; j++)
1580 write32((void *)(0x04000000 + channel_offset + 4 * j), j & 2 ? b : a);
Angel Pons7c49cb82020-03-16 23:17:32 +01001581
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001582 sfence();
1583}
1584
Angel Pons88521882020-01-05 20:21:20 +01001585static int num_of_channels(const ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001586{
1587 int ret = 0;
1588 int channel;
1589 FOR_ALL_POPULATED_CHANNELS ret++;
1590 return ret;
1591}
1592
Angel Pons88521882020-01-05 20:21:20 +01001593static void fill_pattern1(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001594{
Subrata Banikb1434fc2019-03-15 22:20:41 +05301595 unsigned int j;
Angel Pons891f2bc2020-01-10 01:27:28 +01001596 unsigned int channel_offset = get_precedening_channels(ctrl, channel) * 0x40;
Subrata Banikb1434fc2019-03-15 22:20:41 +05301597 unsigned int channel_step = 0x40 * num_of_channels(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01001598
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001599 for (j = 0; j < 16; j++)
1600 write32((void *)(0x04000000 + channel_offset + j * 4), 0xffffffff);
Angel Pons7c49cb82020-03-16 23:17:32 +01001601
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001602 for (j = 0; j < 16; j++)
1603 write32((void *)(0x04000000 + channel_offset + channel_step + j * 4), 0);
Angel Pons7c49cb82020-03-16 23:17:32 +01001604
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001605 sfence();
1606}
1607
Angel Pons88521882020-01-05 20:21:20 +01001608static void precharge(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001609{
1610 int channel, slotrank, lane;
1611
1612 FOR_ALL_POPULATED_CHANNELS {
1613 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001614 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
1615 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001616 }
1617
1618 program_timings(ctrl, channel);
1619
1620 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001621 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001622
1623 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01001624 write MR3 MPR enable
1625 in this mode only RD and RDA are allowed
1626 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001627 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001628 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001629 1, 3, ctrl->tMOD, SSQ_NA,
1630 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001631 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001632
1633 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001634 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001635 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001636 3, 4, 4, SSQ_RD,
1637 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001638 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001639
1640 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001641 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001642 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001643 1, 4, ctrl->CAS + 8, SSQ_NA,
1644 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001645 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001646
1647 /* DRAM command MRS
1648 * write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001649 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001650 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001651 1, 3, ctrl->tMOD, SSQ_NA,
1652 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001653 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02001654
Angel Pons7c49cb82020-03-16 23:17:32 +01001655 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001656 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001657
Angel Pons88521882020-01-05 20:21:20 +01001658 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001659 }
1660
1661 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01001662 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
1663 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001664 }
1665
1666 program_timings(ctrl, channel);
1667
1668 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01001669 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001670 /* DRAM command MRS
1671 * write MR3 MPR enable
1672 * in this mode only RD and RDA are allowed
1673 * all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02001674 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001675 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001676 1, 3, ctrl->tMOD, SSQ_NA,
1677 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001678 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001679
1680 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001681 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001682 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001683 3, 4, 4, SSQ_RD,
1684 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001685 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001686
1687 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001688 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001689 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001690 1, 4, ctrl->CAS + 8, SSQ_NA,
1691 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001692 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001693
1694 /* DRAM command MRS
1695 * write MR3 MPR disable */
Angel Ponsca00dec2020-05-02 15:04:00 +02001696 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001697 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001698 1, 3, ctrl->tMOD, SSQ_NA,
1699 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001700 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001701
Angel Pons7c49cb82020-03-16 23:17:32 +01001702 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001703 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02001704
Angel Pons88521882020-01-05 20:21:20 +01001705 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001706 }
1707 }
1708}
1709
Angel Pons88521882020-01-05 20:21:20 +01001710static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001711{
1712 /* enable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001713 write_mrreg(ctrl, channel, slotrank, 1, 0x80 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001714
Angel Pons88521882020-01-05 20:21:20 +01001715 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001716 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001717 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001718 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001719 1, 3, ctrl->CWL + ctrl->tWLO, SSQ_WR,
1720 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001721 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001722
1723 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001724 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001725 IOSAV_NOP_ALT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001726 1, 3, ctrl->CAS + 38, SSQ_RD,
1727 4, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001728 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001729
Angel Pons7c49cb82020-03-16 23:17:32 +01001730 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001731 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02001732
Angel Pons88521882020-01-05 20:21:20 +01001733 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001734
1735 /* disable DQs on this slotrank */
Angel Pons891f2bc2020-01-10 01:27:28 +01001736 write_mrreg(ctrl, channel, slotrank, 1, 0x1080 | make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001737}
1738
1739static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)
1740{
1741 int timB;
1742 int statistics[NUM_LANES][128];
1743 int lane;
1744
Angel Pons88521882020-01-05 20:21:20 +01001745 MCHBAR32(GDCRTRAININGMOD) = 0x108052 | (slotrank << 2);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001746
1747 for (timB = 0; timB < 128; timB++) {
1748 FOR_ALL_LANES {
1749 ctrl->timings[channel][slotrank].lanes[lane].timB = timB;
1750 }
1751 program_timings(ctrl, channel);
1752
1753 test_timB(ctrl, channel, slotrank);
1754
1755 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001756 statistics[lane][timB] = !((MCHBAR32(lane_base[lane] +
1757 GDCRTRAININGRESULT(channel, (timB / 32) & 1)) >>
1758 (timB % 32)) & 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001759 }
1760 }
1761 FOR_ALL_LANES {
1762 struct run rn = get_longest_zero_run(statistics[lane], 128);
Angel Pons7c49cb82020-03-16 23:17:32 +01001763 /*
1764 * timC is a direct function of timB's 6 LSBs. Some tests increments the value
1765 * of timB by a small value, which might cause the 6-bit value to overflow if
1766 * it's close to 0x3f. Increment the value by a small offset if it's likely
1767 * to overflow, to make sure it won't overflow while running tests and bricks
1768 * the system due to a non matching timC.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001769 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001770 * TODO: find out why some tests (edge write discovery) increment timB.
1771 */
1772 if ((rn.start & 0x3f) == 0x3e)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001773 rn.start += 2;
Angel Pons7c49cb82020-03-16 23:17:32 +01001774 else if ((rn.start & 0x3f) == 0x3f)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001775 rn.start += 1;
Angel Pons7c49cb82020-03-16 23:17:32 +01001776
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001777 ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start;
1778 if (rn.all) {
1779 printk(BIOS_EMERG, "timB discovery failed: %d, %d, %d\n",
1780 channel, slotrank, lane);
Angel Pons7c49cb82020-03-16 23:17:32 +01001781
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001782 return MAKE_ERR;
1783 }
Patrick Rudolph368b6152016-11-25 16:36:52 +01001784 printram("timB: %d, %d, %d: 0x%02x-0x%02x-0x%02x\n",
1785 channel, slotrank, lane, rn.start, rn.middle, rn.end);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001786 }
1787 return 0;
1788}
1789
1790static int get_timB_high_adjust(u64 val)
1791{
1792 int i;
1793
1794 /* good */
1795 if (val == 0xffffffffffffffffLL)
1796 return 0;
1797
1798 if (val >= 0xf000000000000000LL) {
1799 /* needs negative adjustment */
1800 for (i = 0; i < 8; i++)
1801 if (val << (8 * (7 - i) + 4))
1802 return -i;
1803 } else {
1804 /* needs positive adjustment */
1805 for (i = 0; i < 8; i++)
1806 if (val >> (8 * (7 - i) + 4))
1807 return i;
1808 }
1809 return 8;
1810}
1811
Angel Pons88521882020-01-05 20:21:20 +01001812static void adjust_high_timB(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001813{
1814 int channel, slotrank, lane, old;
Angel Pons88521882020-01-05 20:21:20 +01001815 MCHBAR32(GDCRTRAININGMOD) = 0x200;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001816 FOR_ALL_POPULATED_CHANNELS {
1817 fill_pattern1(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001818 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001819 }
1820 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
1821
Angel Pons88521882020-01-05 20:21:20 +01001822 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x10001;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001823
Angel Pons88521882020-01-05 20:21:20 +01001824 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001825
1826 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001827 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001828 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001829 1, 3, ctrl->tRCD, SSQ_NA,
1830 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001831 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001832
1833 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001834 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001835 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001836 1, 3, 4, SSQ_WR,
1837 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001838 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001839
1840 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02001841 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001842 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001843 3, 4, 4, SSQ_WR,
1844 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001845 0, 1, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001846
1847 /* DRAM command NOP */
Angel Ponsca00dec2020-05-02 15:04:00 +02001848 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02001849 IOSAV_NOP, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001850 1, 3, ctrl->CWL + ctrl->tWTR + 5, SSQ_WR,
1851 8, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001852 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001853
Angel Pons7c49cb82020-03-16 23:17:32 +01001854 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001855 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001856
Angel Pons88521882020-01-05 20:21:20 +01001857 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001858
1859 /* DRAM command PREA */
Angel Ponsca00dec2020-05-02 15:04:00 +02001860 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001861 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001862 1, 3, ctrl->tRP, SSQ_NA,
1863 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001864 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001865
1866 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02001867 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02001868 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02001869 1, 3, ctrl->tRCD, SSQ_NA,
1870 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001871 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001872
1873 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02001874 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02001875 IOSAV_RD, 3,
Angel Ponsca00dec2020-05-02 15:04:00 +02001876 1, 3, ctrl->tRP +
1877 ctrl->timings[channel][slotrank].roundtrip_latency +
1878 ctrl->timings[channel][slotrank].io_latency, SSQ_RD,
1879 8, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001880 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001881
Angel Pons7c49cb82020-03-16 23:17:32 +01001882 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001883 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02001884
Angel Pons88521882020-01-05 20:21:20 +01001885 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001886 FOR_ALL_LANES {
Felix Heldfb19c8a2020-01-14 21:27:59 +01001887 u64 res = MCHBAR32(lane_base[lane] + GDCRTRAININGRESULT1(channel));
Felix Held283b44662020-01-14 21:14:42 +01001888 res |= ((u64) MCHBAR32(lane_base[lane] +
Felix Heldfb19c8a2020-01-14 21:27:59 +01001889 GDCRTRAININGRESULT2(channel))) << 32;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001890 old = ctrl->timings[channel][slotrank].lanes[lane].timB;
1891 ctrl->timings[channel][slotrank].lanes[lane].timB +=
1892 get_timB_high_adjust(res) * 64;
1893
1894 printram("High adjust %d:%016llx\n", lane, res);
Angel Pons891f2bc2020-01-10 01:27:28 +01001895 printram("Bval+: %d, %d, %d, %x -> %x\n", channel, slotrank, lane,
1896 old, ctrl->timings[channel][slotrank].lanes[lane].timB);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001897 }
1898 }
Angel Pons88521882020-01-05 20:21:20 +01001899 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001900}
1901
Angel Pons88521882020-01-05 20:21:20 +01001902static void write_op(ramctr_timing *ctrl, int channel)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001903{
1904 int slotrank;
1905
Angel Pons88521882020-01-05 20:21:20 +01001906 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001907
1908 /* choose an existing rank. */
1909 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
1910
Angel Pons69e17142020-03-23 12:26:29 +01001911 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02001912 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001913 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02001914 1, 4, 4, SSQ_NA,
1915 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02001916 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001917
Angel Pons7c49cb82020-03-16 23:17:32 +01001918 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001919 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02001920
Angel Pons88521882020-01-05 20:21:20 +01001921 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001922}
1923
Angel Pons7c49cb82020-03-16 23:17:32 +01001924/*
1925 * Compensate the skew between CMD/ADDR/CLK and DQ/DQS lanes.
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001926 *
Angel Pons7c49cb82020-03-16 23:17:32 +01001927 * Since DDR3 uses a fly-by topology, the data and strobes signals reach the chips at different
1928 * times with respect to command, address and clock signals. By delaying either all DQ/DQS or
1929 * all CMD/ADDR/CLK signals, a full phase shift can be introduced. It is assumed that the
1930 * CLK/ADDR/CMD signals have the same routing delay.
1931 *
1932 * To find the required phase shift the DRAM is placed in "write leveling" mode. In this mode,
1933 * the DRAM-chip samples the CLK on every DQS edge and feeds back the sampled value on the data
1934 * lanes (DQ).
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001935 */
Angel Pons88521882020-01-05 20:21:20 +01001936int write_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001937{
1938 int channel, slotrank, lane;
1939 int err;
1940
1941 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001942 MCHBAR32_OR(TC_RWP_ch(channel), 0x8000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001943
1944 FOR_ALL_POPULATED_CHANNELS {
1945 write_op(ctrl, channel);
Angel Pons88521882020-01-05 20:21:20 +01001946 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001947 }
1948
Angel Pons7c49cb82020-03-16 23:17:32 +01001949 /* Refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01001950 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001951 FOR_ALL_POPULATED_CHANNELS {
1952 write_op(ctrl, channel);
1953 }
1954
Angel Pons7c49cb82020-03-16 23:17:32 +01001955 /* Enable write leveling on all ranks
1956 Disable all DQ outputs
1957 Only NOP is allowed in this mode */
1958 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
1959 write_mrreg(ctrl, channel, slotrank, 1,
Felix Held2bb3cdf2018-07-28 00:23:59 +02001960 make_mr1(ctrl, slotrank, channel) | 0x1080);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001961
Angel Pons88521882020-01-05 20:21:20 +01001962 MCHBAR32(GDCRTRAININGMOD) = 0x108052;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001963
1964 toggle_io_reset();
1965
Angel Pons7c49cb82020-03-16 23:17:32 +01001966 /* Set any valid value for timB, it gets corrected later */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001967 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
1968 err = discover_timB(ctrl, channel, slotrank);
1969 if (err)
1970 return err;
1971 }
1972
Angel Pons7c49cb82020-03-16 23:17:32 +01001973 /* Disable write leveling on all ranks */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001974 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
Angel Pons7c49cb82020-03-16 23:17:32 +01001975 write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001976
Angel Pons88521882020-01-05 20:21:20 +01001977 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001978
1979 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01001980 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001981
Angel Pons7c49cb82020-03-16 23:17:32 +01001982 /* Refresh enable */
Angel Pons88521882020-01-05 20:21:20 +01001983 MCHBAR32_OR(MC_INIT_STATE_G, 8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001984
1985 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01001986 MCHBAR32_AND(SCHED_CBIT_ch(channel), ~0x00200000);
1987 MCHBAR32(IOSAV_STATUS_ch(channel));
1988 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001989
1990 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02001991 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001992 IOSAV_ZQCS, 0,
Angel Pons2be59002020-05-02 22:15:03 +02001993 1, 4, 101, SSQ_NA,
Angel Ponsca00dec2020-05-02 15:04:00 +02001994 0, 6, 0, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02001995 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01001996
Angel Pons7c49cb82020-03-16 23:17:32 +01001997 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02001998 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02001999
Angel Pons88521882020-01-05 20:21:20 +01002000 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002001 }
2002
2003 toggle_io_reset();
2004
2005 printram("CPE\n");
2006 precharge(ctrl);
2007 printram("CPF\n");
2008
2009 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002010 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002011 }
2012
2013 FOR_ALL_POPULATED_CHANNELS {
2014 fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555);
Angel Pons88521882020-01-05 20:21:20 +01002015 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002016 }
2017
2018 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2019 err = discover_timC(ctrl, channel, slotrank);
2020 if (err)
2021 return err;
2022 }
2023
2024 FOR_ALL_POPULATED_CHANNELS
2025 program_timings(ctrl, channel);
2026
2027 /* measure and adjust timB timings */
2028 adjust_high_timB(ctrl);
2029
2030 FOR_ALL_POPULATED_CHANNELS
2031 program_timings(ctrl, channel);
2032
2033 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002034 MCHBAR32_AND(IOSAV_By_BW_MASK_ch(channel, lane), 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002035 }
2036 return 0;
2037}
2038
Angel Pons88521882020-01-05 20:21:20 +01002039static int test_320c(ramctr_timing *ctrl, int channel, int slotrank)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002040{
2041 struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank];
2042 int timC_delta;
2043 int lanes_ok = 0;
2044 int ctr = 0;
2045 int lane;
2046
2047 for (timC_delta = -5; timC_delta <= 5; timC_delta++) {
2048 FOR_ALL_LANES {
2049 ctrl->timings[channel][slotrank].lanes[lane].timC =
2050 saved_rt.lanes[lane].timC + timC_delta;
2051 }
2052 program_timings(ctrl, channel);
2053 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002054 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002055 }
2056
Angel Pons88521882020-01-05 20:21:20 +01002057 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002058
Angel Pons88521882020-01-05 20:21:20 +01002059 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002060 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002061 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002062 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002063 8, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
2064 ctr, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002065 0, 0, 1, 0, 18, 0, 0, 0);
Felix Held9fe248f2018-07-31 20:59:45 +02002066
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002067 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002068 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002069 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002070 32, 4, ctrl->CWL + ctrl->tWTR + 8, SSQ_WR,
2071 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002072 0, 1, 0, 0, 18, 3, 0, 2);
Angel Ponsca00dec2020-05-02 15:04:00 +02002073
Angel Ponsc36cd072020-05-02 16:51:39 +02002074 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 1)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002075
2076 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002077 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002078 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002079 32, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
2080 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002081 0, 1, 0, 0, 18, 3, 0, 2);
Angel Ponsca00dec2020-05-02 15:04:00 +02002082
Angel Ponsc36cd072020-05-02 16:51:39 +02002083 MCHBAR32(IOSAV_n_ADDRESS_LFSR_ch(channel, 2)) = 0x389abcd;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002084
2085 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002086 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002087 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002088 1, 4, 15, SSQ_NA,
2089 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002090 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002091
Angel Pons7c49cb82020-03-16 23:17:32 +01002092 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02002093 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002094
Angel Pons88521882020-01-05 20:21:20 +01002095 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002096 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002097 u32 r32 = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002098
2099 if (r32 == 0)
2100 lanes_ok |= 1 << lane;
2101 }
2102 ctr++;
Patrick Rudolphdd662872017-10-28 18:20:11 +02002103 if (lanes_ok == ((1 << ctrl->lanes) - 1))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002104 break;
2105 }
2106
2107 ctrl->timings[channel][slotrank] = saved_rt;
2108
Patrick Rudolphdd662872017-10-28 18:20:11 +02002109 return lanes_ok != ((1 << ctrl->lanes) - 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002110}
2111
Angel Pons88521882020-01-05 20:21:20 +01002112static void fill_pattern5(ramctr_timing *ctrl, int channel, int patno)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002113{
Subrata Banikb1434fc2019-03-15 22:20:41 +05302114 unsigned int i, j;
Angel Pons7c49cb82020-03-16 23:17:32 +01002115 unsigned int offset = get_precedening_channels(ctrl, channel) * 0x40;
2116 unsigned int step = 0x40 * num_of_channels(ctrl);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002117
2118 if (patno) {
2119 u8 base8 = 0x80 >> ((patno - 1) % 8);
2120 u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24);
2121 for (i = 0; i < 32; i++) {
2122 for (j = 0; j < 16; j++) {
2123 u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01002124
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002125 if (invert[patno - 1][i] & (1 << (j / 2)))
2126 val = ~val;
Angel Pons7c49cb82020-03-16 23:17:32 +01002127
2128 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002129 }
2130 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002131 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01002132 for (i = 0; i < ARRAY_SIZE(pattern); i++) {
2133 for (j = 0; j < 16; j++) {
2134 const u32 val = pattern[i][j];
2135 write32((void *)((1 << 26) + offset + i * step + j * 4), val);
2136 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002137 }
2138 sfence();
2139 }
2140}
2141
Angel Pons88521882020-01-05 20:21:20 +01002142static void reprogram_320c(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002143{
2144 int channel, slotrank;
2145
2146 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002147 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002148
Angel Pons7c49cb82020-03-16 23:17:32 +01002149 /* Choose an existing rank */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002150 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2151
2152 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02002153 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002154 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02002155 1, 4, 4, SSQ_NA,
2156 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002157 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002158
Angel Pons7c49cb82020-03-16 23:17:32 +01002159 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02002160 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002161
Angel Pons88521882020-01-05 20:21:20 +01002162 wait_for_iosav(channel);
2163 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002164 }
2165
2166 /* refresh disable */
Angel Pons88521882020-01-05 20:21:20 +01002167 MCHBAR32_AND(MC_INIT_STATE_G, ~8);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002168 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01002169 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002170
2171 /* choose an existing rank. */
2172 slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0;
2173
2174 /* DRAM command ZQCS */
Angel Ponsca00dec2020-05-02 15:04:00 +02002175 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002176 IOSAV_ZQCS, 0,
Angel Ponsca00dec2020-05-02 15:04:00 +02002177 1, 4, 4, SSQ_NA,
2178 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002179 0, 0, 0, 0, 31, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002180
Angel Pons7c49cb82020-03-16 23:17:32 +01002181 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02002182 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002183
Angel Pons88521882020-01-05 20:21:20 +01002184 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002185 }
2186
Angel Pons7c49cb82020-03-16 23:17:32 +01002187 /* JEDEC reset */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002188 dram_jedecreset(ctrl);
Angel Pons7c49cb82020-03-16 23:17:32 +01002189
2190 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002191 dram_mrscommands(ctrl);
2192
2193 toggle_io_reset();
2194}
2195
2196#define MIN_C320C_LEN 13
2197
2198static int try_cmd_stretch(ramctr_timing *ctrl, int channel, int cmd_stretch)
2199{
2200 struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS];
2201 int slotrank;
2202 int c320c;
2203 int stat[NUM_SLOTRANKS][256];
2204 int delta = 0;
2205
2206 printram("Trying cmd_stretch %d on channel %d\n", cmd_stretch, channel);
2207
2208 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002209 saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002210 }
2211
2212 ctrl->cmd_stretch[channel] = cmd_stretch;
2213
Angel Pons88521882020-01-05 20:21:20 +01002214 MCHBAR32(TC_RAP_ch(channel)) =
Angel Pons7c49cb82020-03-16 23:17:32 +01002215 (ctrl->tRRD << 0)
2216 | (ctrl->tRTP << 4)
2217 | (ctrl->tCKE << 8)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002218 | (ctrl->tWTR << 12)
2219 | (ctrl->tFAW << 16)
Angel Pons7c49cb82020-03-16 23:17:32 +01002220 | (ctrl->tWR << 24)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002221 | (ctrl->cmd_stretch[channel] << 30);
2222
2223 if (ctrl->cmd_stretch[channel] == 2)
2224 delta = 2;
2225 else if (ctrl->cmd_stretch[channel] == 0)
2226 delta = 4;
2227
2228 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002229 ctrl->timings[channel][slotrank].roundtrip_latency -= delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002230 }
2231
2232 for (c320c = -127; c320c <= 127; c320c++) {
2233 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002234 ctrl->timings[channel][slotrank].pi_coding = c320c;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002235 }
2236 program_timings(ctrl, channel);
2237 reprogram_320c(ctrl);
2238 FOR_ALL_POPULATED_RANKS {
Angel Pons891f2bc2020-01-10 01:27:28 +01002239 stat[slotrank][c320c + 127] = test_320c(ctrl, channel, slotrank);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002240 }
2241 }
2242 FOR_ALL_POPULATED_RANKS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002243 struct run rn = get_longest_zero_run(stat[slotrank], 255);
2244
Angel Pons88521882020-01-05 20:21:20 +01002245 ctrl->timings[channel][slotrank].pi_coding = rn.middle - 127;
Patrick Rudolph368b6152016-11-25 16:36:52 +01002246 printram("cmd_stretch: %d, %d: 0x%02x-0x%02x-0x%02x\n",
2247 channel, slotrank, rn.start, rn.middle, rn.end);
Angel Pons7c49cb82020-03-16 23:17:32 +01002248
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002249 if (rn.all || rn.length < MIN_C320C_LEN) {
2250 FOR_ALL_POPULATED_RANKS {
2251 ctrl->timings[channel][slotrank] =
2252 saved_timings[channel][slotrank];
2253 }
2254 return MAKE_ERR;
2255 }
2256 }
2257
2258 return 0;
2259}
2260
Angel Pons7c49cb82020-03-16 23:17:32 +01002261/*
2262 * Adjust CMD phase shift and try multiple command rates.
2263 * A command rate of 2T doubles the time needed for address and command decode.
2264 */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002265int command_training(ramctr_timing *ctrl)
2266{
2267 int channel;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002268
2269 FOR_ALL_POPULATED_CHANNELS {
2270 fill_pattern5(ctrl, channel, 0);
Angel Pons88521882020-01-05 20:21:20 +01002271 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002272 }
2273
2274 FOR_ALL_POPULATED_CHANNELS {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002275 int cmdrate, err;
2276
2277 /*
2278 * Dual DIMM per channel:
Angel Pons7c49cb82020-03-16 23:17:32 +01002279 * Issue:
2280 * While c320c discovery seems to succeed raminit will fail in write training.
2281 *
2282 * Workaround:
2283 * Skip 1T in dual DIMM mode, that's only supported by a few DIMMs.
2284 * Only try 1T mode for XMP DIMMs that request it in dual DIMM mode.
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002285 *
2286 * Single DIMM per channel:
2287 * Try command rate 1T and 2T
2288 */
2289 cmdrate = ((ctrl->rankmap[channel] & 0x5) == 0x5);
Dan Elkoubydabebc32018-04-13 18:47:10 +03002290 if (ctrl->tCMD)
2291 /* XMP gives the CMD rate in clock ticks, not ns */
2292 cmdrate = MIN(DIV_ROUND_UP(ctrl->tCMD, 256) - 1, 1);
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002293
Elyes HAOUASadda3f812018-01-31 23:02:35 +01002294 for (; cmdrate < 2; cmdrate++) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002295 err = try_cmd_stretch(ctrl, channel, cmdrate << 1);
2296
2297 if (!err)
2298 break;
2299 }
2300
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002301 if (err) {
Patrick Rudolph58d16af2017-06-19 19:33:12 +02002302 printk(BIOS_EMERG, "c320c discovery failed\n");
2303 return err;
2304 }
2305
Angel Pons891f2bc2020-01-10 01:27:28 +01002306 printram("Using CMD rate %uT on channel %u\n", cmdrate + 1, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002307 }
2308
2309 FOR_ALL_POPULATED_CHANNELS
2310 program_timings(ctrl, channel);
2311
2312 reprogram_320c(ctrl);
2313 return 0;
2314}
2315
Angel Pons891f2bc2020-01-10 01:27:28 +01002316static int discover_edges_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002317{
2318 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002319 int stats[NUM_LANES][MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002320 int lane;
2321
2322 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2323 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002324 ctrl->timings[channel][slotrank].lanes[lane].rising = edge;
Angel Pons891f2bc2020-01-10 01:27:28 +01002325 ctrl->timings[channel][slotrank].lanes[lane].falling = edge;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002326 }
2327 program_timings(ctrl, channel);
2328
2329 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002330 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2331 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002332 }
2333
Angel Pons88521882020-01-05 20:21:20 +01002334 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002335
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002336 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002337 write MR3 MPR enable
2338 in this mode only RD and RDA are allowed
2339 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002340 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002341 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002342 1, 3, ctrl->tMOD, SSQ_NA,
2343 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002344 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002345
2346 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002347 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002348 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002349 500, 4, 4, SSQ_RD,
2350 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002351 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002352
2353 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002354 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002355 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002356 1, 4, ctrl->CAS + 8, SSQ_NA,
2357 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002358 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002359
2360 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002361 MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002362 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002363 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002364 1, 3, ctrl->tMOD, SSQ_NA,
2365 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002366 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002367
Angel Pons7c49cb82020-03-16 23:17:32 +01002368 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02002369 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002370
Angel Pons88521882020-01-05 20:21:20 +01002371 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002372
2373 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002374 stats[lane][edge] = MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002375 }
2376 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002377
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002378 FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002379 struct run rn = get_longest_zero_run(stats[lane], MAX_EDGE_TIMING + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002380 edges[lane] = rn.middle;
Angel Pons7c49cb82020-03-16 23:17:32 +01002381
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002382 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002383 printk(BIOS_EMERG, "edge discovery failed: %d, %d, %d\n", channel,
2384 slotrank, lane);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002385 return MAKE_ERR;
2386 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002387 printram("eval %d, %d, %d: %02x\n", channel, slotrank, lane, edges[lane]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002388 }
2389 return 0;
2390}
2391
2392int discover_edges(ramctr_timing *ctrl)
2393{
2394 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2395 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2396 int channel, slotrank, lane;
2397 int err;
2398
Angel Pons88521882020-01-05 20:21:20 +01002399 MCHBAR32(GDCRTRAININGMOD) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002400
2401 toggle_io_reset();
2402
2403 FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002404 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002405 }
2406
2407 FOR_ALL_POPULATED_CHANNELS {
2408 fill_pattern0(ctrl, channel, 0, 0);
Angel Pons88521882020-01-05 20:21:20 +01002409 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002410 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002411 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002412 }
2413
2414 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002415 ctrl->timings[channel][slotrank].lanes[lane].falling = 16;
2416 ctrl->timings[channel][slotrank].lanes[lane].rising = 16;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002417 }
2418
2419 program_timings(ctrl, channel);
2420
2421 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002422 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002423
2424 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002425 MR3 enable MPR
2426 write MR3 MPR enable
2427 in this mode only RD and RDA are allowed
2428 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002429 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002430 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002431 1, 3, ctrl->tMOD, SSQ_NA,
2432 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002433 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002434
2435 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002436 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002437 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002438 3, 4, 4, SSQ_RD,
2439 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002440 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002441
2442 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002443 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002444 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002445 1, 4, ctrl->CAS + 8, SSQ_NA,
2446 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002447 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002448
2449 /* DRAM command MRS
2450 * MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002451 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002452 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002453 1, 3, ctrl->tMOD, SSQ_NA,
2454 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002455 0, 0, 0, 0, 0, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002456
Angel Pons7c49cb82020-03-16 23:17:32 +01002457 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02002458 iosav_run_once(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002459
Angel Pons88521882020-01-05 20:21:20 +01002460 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002461 }
2462
2463 /* XXX: check any measured value ? */
2464
2465 FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons891f2bc2020-01-10 01:27:28 +01002466 ctrl->timings[channel][slotrank].lanes[lane].falling = 48;
Angel Pons7c49cb82020-03-16 23:17:32 +01002467 ctrl->timings[channel][slotrank].lanes[lane].rising = 48;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002468 }
2469
2470 program_timings(ctrl, channel);
2471
2472 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01002473 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002474
2475 /* DRAM command MRS
Angel Pons7c49cb82020-03-16 23:17:32 +01002476 MR3 enable MPR
2477 write MR3 MPR enable
2478 in this mode only RD and RDA are allowed
2479 all reads return a predefined pattern */
Angel Ponsca00dec2020-05-02 15:04:00 +02002480 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002481 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002482 1, 3, ctrl->tMOD, SSQ_NA,
2483 4, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002484 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002485
2486 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002487 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002488 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002489 3, 4, 4, SSQ_RD,
2490 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002491 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002492
2493 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002494 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002495 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002496 1, 4, ctrl->CAS + 8, SSQ_NA,
2497 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002498 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002499
2500 /* DRAM command MRS
2501 * MR3 disable MPR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002502 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002503 IOSAV_MRS, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002504 1, 3, ctrl->tMOD, SSQ_NA,
2505 0, 6, 3, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002506 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002507
Angel Pons7c49cb82020-03-16 23:17:32 +01002508 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02002509 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002510
Angel Pons88521882020-01-05 20:21:20 +01002511 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002512 }
2513
2514 /* XXX: check any measured value ? */
2515
2516 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002517 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) =
Angel Pons891f2bc2020-01-10 01:27:28 +01002518 ~MCHBAR32(IOSAV_By_BW_SERROR_ch(channel, lane)) & 0xff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002519 }
2520
2521 fill_pattern0(ctrl, channel, 0, 0xffffffff);
Angel Pons88521882020-01-05 20:21:20 +01002522 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002523 }
2524
Angel Pons0c3936e2020-03-22 12:49:27 +01002525 /*
2526 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2527 * also use a single loop. It would seem that it is a debugging configuration.
2528 */
Angel Pons88521882020-01-05 20:21:20 +01002529 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2530 printram("discover falling edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002531
2532 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2533 err = discover_edges_real(ctrl, channel, slotrank,
Felix Held2bb3cdf2018-07-28 00:23:59 +02002534 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002535 if (err)
2536 return err;
2537 }
2538
Angel Pons88521882020-01-05 20:21:20 +01002539 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2540 printram("discover rising edges:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002541
2542 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2543 err = discover_edges_real(ctrl, channel, slotrank,
2544 rising_edges[channel][slotrank]);
2545 if (err)
2546 return err;
2547 }
2548
Angel Pons88521882020-01-05 20:21:20 +01002549 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002550
2551 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2552 ctrl->timings[channel][slotrank].lanes[lane].falling =
2553 falling_edges[channel][slotrank][lane];
2554 ctrl->timings[channel][slotrank].lanes[lane].rising =
2555 rising_edges[channel][slotrank][lane];
2556 }
2557
2558 FOR_ALL_POPULATED_CHANNELS {
2559 program_timings(ctrl, channel);
2560 }
2561
2562 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002563 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002564 }
2565 return 0;
2566}
2567
Angel Pons7c49cb82020-03-16 23:17:32 +01002568static int discover_edges_write_real(ramctr_timing *ctrl, int channel, int slotrank, int *edges)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002569{
2570 int edge;
Angel Pons7c49cb82020-03-16 23:17:32 +01002571 u32 raw_stats[MAX_EDGE_TIMING + 1];
2572 int stats[MAX_EDGE_TIMING + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002573 const int reg3000b24[] = { 0, 0xc, 0x2c };
2574 int lane, i;
2575 int lower[NUM_LANES];
2576 int upper[NUM_LANES];
2577 int pat;
2578
2579 FOR_ALL_LANES {
2580 lower[lane] = 0;
2581 upper[lane] = MAX_EDGE_TIMING;
2582 }
2583
2584 for (i = 0; i < 3; i++) {
Angel Pons88521882020-01-05 20:21:20 +01002585 MCHBAR32(GDCRTRAININGMOD_ch(channel)) = reg3000b24[i] << 24;
Angel Pons7c49cb82020-03-16 23:17:32 +01002586 printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), reg3000b24[i] << 24);
2587
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002588 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2589 fill_pattern5(ctrl, channel, pat);
Angel Pons88521882020-01-05 20:21:20 +01002590 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002591 printram("using pattern %d\n", pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002592
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002593 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) {
2594 FOR_ALL_LANES {
2595 ctrl->timings[channel][slotrank].lanes[lane].
2596 rising = edge;
2597 ctrl->timings[channel][slotrank].lanes[lane].
2598 falling = edge;
2599 }
2600 program_timings(ctrl, channel);
2601
2602 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002603 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0;
2604 MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002605 }
Angel Pons88521882020-01-05 20:21:20 +01002606 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002607
2608 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002609 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002610 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002611 4, MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), ctrl->tRCD, SSQ_NA,
2612 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002613 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002614
2615 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002616 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002617 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002618 32, 20, ctrl->tWTR + ctrl->CWL + 8, SSQ_WR,
2619 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002620 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002621
2622 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002623 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002624 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002625 32, 20, MAX(ctrl->tRTP, 8), SSQ_RD,
2626 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002627 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002628
2629 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002630 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002631 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002632 1, 3, ctrl->tRP, SSQ_NA,
2633 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002634 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002635
Angel Pons7c49cb82020-03-16 23:17:32 +01002636 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02002637 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002638
Angel Pons88521882020-01-05 20:21:20 +01002639 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002640 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002641 MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002642 }
2643
Angel Pons7c49cb82020-03-16 23:17:32 +01002644 /* FIXME: This register only exists on Ivy Bridge */
Angel Pons098240eb2020-03-22 12:55:32 +01002645 raw_stats[edge] = MCHBAR32(IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002646 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002647
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002648 FOR_ALL_LANES {
2649 struct run rn;
2650 for (edge = 0; edge <= MAX_EDGE_TIMING; edge++)
Angel Pons7c49cb82020-03-16 23:17:32 +01002651 stats[edge] = !!(raw_stats[edge] & (1 << lane));
2652
2653 rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
2654
2655 printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, "
2656 "0x%02x-0x%02x\n", channel, slotrank, i, rn.start,
2657 rn.middle, rn.end, rn.start + ctrl->edge_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002658 rn.end - ctrl->edge_offset[i]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002659
2660 lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]);
2661 upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]);
2662
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002663 edges[lane] = (lower[lane] + upper[lane]) / 2;
2664 if (rn.all || (lower[lane] > upper[lane])) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002665 printk(BIOS_EMERG, "edge write discovery failed: "
2666 "%d, %d, %d\n", channel, slotrank, lane);
2667
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002668 return MAKE_ERR;
2669 }
2670 }
2671 }
2672 }
2673
Angel Pons88521882020-01-05 20:21:20 +01002674 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002675 printram("CPA\n");
2676 return 0;
2677}
2678
2679int discover_edges_write(ramctr_timing *ctrl)
2680{
2681 int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
Angel Pons7c49cb82020-03-16 23:17:32 +01002682 int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2683 int channel, slotrank, lane, err;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002684
Angel Pons7c49cb82020-03-16 23:17:32 +01002685 /*
2686 * FIXME: Under some conditions, vendor BIOS sets both edges to the same value. It will
2687 * also use a single loop. It would seem that it is a debugging configuration.
2688 */
Angel Pons88521882020-01-05 20:21:20 +01002689 MCHBAR32(IOSAV_DC_MASK) = 0x300;
2690 printram("discover falling edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x300);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002691
2692 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2693 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002694 falling_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002695 if (err)
2696 return err;
2697 }
2698
Angel Pons88521882020-01-05 20:21:20 +01002699 MCHBAR32(IOSAV_DC_MASK) = 0x200;
2700 printram("discover rising edges write:\n[%x] = %x\n", IOSAV_DC_MASK, 0x200);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002701
2702 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2703 err = discover_edges_write_real(ctrl, channel, slotrank,
Angel Pons7c49cb82020-03-16 23:17:32 +01002704 rising_edges[channel][slotrank]);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002705 if (err)
2706 return err;
2707 }
2708
Angel Pons88521882020-01-05 20:21:20 +01002709 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002710
2711 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2712 ctrl->timings[channel][slotrank].lanes[lane].falling =
Angel Pons7c49cb82020-03-16 23:17:32 +01002713 falling_edges[channel][slotrank][lane];
2714
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002715 ctrl->timings[channel][slotrank].lanes[lane].rising =
Angel Pons7c49cb82020-03-16 23:17:32 +01002716 rising_edges[channel][slotrank][lane];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002717 }
2718
2719 FOR_ALL_POPULATED_CHANNELS
2720 program_timings(ctrl, channel);
2721
2722 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002723 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002724 }
2725 return 0;
2726}
2727
2728static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank)
2729{
Angel Pons88521882020-01-05 20:21:20 +01002730 wait_for_iosav(channel);
Angel Pons7c49cb82020-03-16 23:17:32 +01002731
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002732 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002733 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002734 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002735 4, MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD), ctrl->tRCD, SSQ_NA,
2736 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002737 0, 0, 1, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002738
2739 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002740 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002741 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002742 480, 4, ctrl->tWTR + ctrl->CWL + 8, SSQ_WR,
2743 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002744 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002745
2746 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002747 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002748 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002749 480, 4, MAX(ctrl->tRTP, 8), SSQ_RD,
2750 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002751 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002752
2753 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002754 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002755 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002756 1, 4, ctrl->tRP, SSQ_NA,
2757 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002758 0, 0, 0, 0, 0, 0, 0, 0);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002759
Angel Pons7c49cb82020-03-16 23:17:32 +01002760 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02002761 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002762
Angel Pons88521882020-01-05 20:21:20 +01002763 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002764}
2765
2766int discover_timC_write(ramctr_timing *ctrl)
2767{
Angel Pons7c49cb82020-03-16 23:17:32 +01002768 const u8 rege3c_b24[3] = { 0, 0x0f, 0x2f };
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002769 int i, pat;
2770
2771 int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2772 int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES];
2773 int channel, slotrank, lane;
2774
2775 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
2776 lower[channel][slotrank][lane] = 0;
2777 upper[channel][slotrank][lane] = MAX_TIMC;
2778 }
2779
Angel Pons88521882020-01-05 20:21:20 +01002780 /*
2781 * Enable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2782 * FIXME: This must only be done on Ivy Bridge.
2783 */
2784 MCHBAR32(MCMNTS_SPARE) = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002785 printram("discover timC write:\n");
2786
2787 for (i = 0; i < 3; i++)
2788 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002789
2790 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
2791 MCHBAR32_AND_OR(GDCRCMDDEBUGMUXCFG_Cz_S(channel),
2792 ~0x3f000000, rege3c_b24[i] << 24);
2793
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002794 udelay(2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002795
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002796 for (pat = 0; pat < NUM_PATTERNS; pat++) {
2797 FOR_ALL_POPULATED_RANKS {
2798 int timC;
Angel Pons7c49cb82020-03-16 23:17:32 +01002799 u32 raw_stats[MAX_TIMC + 1];
2800 int stats[MAX_TIMC + 1];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002801
2802 /* Make sure rn.start < rn.end */
Angel Pons7c49cb82020-03-16 23:17:32 +01002803 stats[MAX_TIMC] = 1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002804
2805 fill_pattern5(ctrl, channel, pat);
Angel Pons7c49cb82020-03-16 23:17:32 +01002806 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0x1f;
2807
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002808 for (timC = 0; timC < MAX_TIMC; timC++) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002809 FOR_ALL_LANES {
2810 ctrl->timings[channel][slotrank]
2811 .lanes[lane].timC = timC;
2812 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002813 program_timings(ctrl, channel);
2814
2815 test_timC_write (ctrl, channel, slotrank);
2816
Angel Pons7c49cb82020-03-16 23:17:32 +01002817 /* FIXME: Another IVB-only register! */
Angel Pons098240eb2020-03-22 12:55:32 +01002818 raw_stats[timC] = MCHBAR32(
2819 IOSAV_BYTE_SERROR_C_ch(channel));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002820 }
2821 FOR_ALL_LANES {
2822 struct run rn;
Angel Pons7c49cb82020-03-16 23:17:32 +01002823 for (timC = 0; timC < MAX_TIMC; timC++) {
2824 stats[timC] = !!(raw_stats[timC]
2825 & (1 << lane));
2826 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002827
Angel Pons7c49cb82020-03-16 23:17:32 +01002828 rn = get_longest_zero_run(stats, MAX_TIMC + 1);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002829 if (rn.all) {
Angel Pons7c49cb82020-03-16 23:17:32 +01002830 printk(BIOS_EMERG,
2831 "timC write discovery failed: "
2832 "%d, %d, %d\n", channel,
2833 slotrank, lane);
2834
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002835 return MAKE_ERR;
2836 }
Angel Pons7c49cb82020-03-16 23:17:32 +01002837 printram("timC: %d, %d, %d: "
2838 "0x%02x-0x%02x-0x%02x, "
2839 "0x%02x-0x%02x\n", channel, slotrank,
2840 i, rn.start, rn.middle, rn.end,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002841 rn.start + ctrl->timC_offset[i],
Angel Pons7c49cb82020-03-16 23:17:32 +01002842 rn.end - ctrl->timC_offset[i]);
2843
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002844 lower[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002845 MAX(rn.start + ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002846 lower[channel][slotrank][lane]);
Angel Pons7c49cb82020-03-16 23:17:32 +01002847
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002848 upper[channel][slotrank][lane] =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002849 MIN(rn.end - ctrl->timC_offset[i],
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002850 upper[channel][slotrank][lane]);
2851
2852 }
2853 }
2854 }
2855 }
2856
2857 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01002858 /* FIXME: Setting the Write VREF must only be done on Ivy Bridge */
Angel Pons88521882020-01-05 20:21:20 +01002859 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002860 udelay(2);
2861 }
2862
Angel Pons88521882020-01-05 20:21:20 +01002863 /*
2864 * Disable IOSAV_n_SPECIAL_COMMAND_ADDR optimization.
2865 * FIXME: This must only be done on Ivy Bridge.
2866 */
2867 MCHBAR32(MCMNTS_SPARE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002868
2869 printram("CPB\n");
2870
2871 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons7c49cb82020-03-16 23:17:32 +01002872 printram("timC %d, %d, %d: %x\n", channel, slotrank, lane,
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002873 (lower[channel][slotrank][lane] +
2874 upper[channel][slotrank][lane]) / 2);
Angel Pons7c49cb82020-03-16 23:17:32 +01002875
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002876 ctrl->timings[channel][slotrank].lanes[lane].timC =
2877 (lower[channel][slotrank][lane] +
2878 upper[channel][slotrank][lane]) / 2;
2879 }
2880 FOR_ALL_POPULATED_CHANNELS {
2881 program_timings(ctrl, channel);
2882 }
2883 return 0;
2884}
2885
Angel Pons88521882020-01-05 20:21:20 +01002886void normalize_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002887{
2888 int channel, slotrank, lane;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002889 int mat;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002890
2891 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
2892 int delta;
Patrick Rudolph3c8cb972016-11-25 16:00:01 +01002893 mat = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002894 FOR_ALL_LANES mat =
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01002895 MAX(ctrl->timings[channel][slotrank].lanes[lane].timA, mat);
Patrick Rudolph413edc82016-11-25 15:40:07 +01002896 printram("normalize %d, %d, %d: mat %d\n",
2897 channel, slotrank, lane, mat);
2898
Felix Heldef4fe3e2019-12-31 14:15:05 +01002899 delta = (mat >> 6) - ctrl->timings[channel][slotrank].io_latency;
Patrick Rudolph413edc82016-11-25 15:40:07 +01002900 printram("normalize %d, %d, %d: delta %d\n",
2901 channel, slotrank, lane, delta);
2902
Angel Pons88521882020-01-05 20:21:20 +01002903 ctrl->timings[channel][slotrank].roundtrip_latency += delta;
Felix Heldef4fe3e2019-12-31 14:15:05 +01002904 ctrl->timings[channel][slotrank].io_latency += delta;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002905 }
2906
2907 FOR_ALL_POPULATED_CHANNELS {
2908 program_timings(ctrl, channel);
2909 }
2910}
2911
Angel Pons88521882020-01-05 20:21:20 +01002912void write_controller_mr(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002913{
2914 int channel, slotrank;
2915
2916 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS {
Felix Heldfb19c8a2020-01-14 21:27:59 +01002917 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT1(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002918 make_mr0(ctrl, slotrank);
Felix Heldfb19c8a2020-01-14 21:27:59 +01002919 MCHBAR32(lane_base[slotrank] + GDCRTRAININGRESULT2(channel)) =
Felix Held2bb3cdf2018-07-28 00:23:59 +02002920 make_mr1(ctrl, slotrank, channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002921 }
2922}
2923
2924int channel_test(ramctr_timing *ctrl)
2925{
2926 int channel, slotrank, lane;
2927
2928 slotrank = 0;
2929 FOR_ALL_POPULATED_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01002930 if (MCHBAR32(MC_INIT_STATE_ch(channel)) & 0xa000) {
Angel Pons891f2bc2020-01-10 01:27:28 +01002931 printk(BIOS_EMERG, "Mini channel test failed (1): %d\n", channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002932 return MAKE_ERR;
2933 }
2934 FOR_ALL_POPULATED_CHANNELS {
2935 fill_pattern0(ctrl, channel, 0x12345678, 0x98765432);
2936
Angel Pons88521882020-01-05 20:21:20 +01002937 MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002938 }
2939
2940 for (slotrank = 0; slotrank < 4; slotrank++)
2941 FOR_ALL_CHANNELS
2942 if (ctrl->rankmap[channel] & (1 << slotrank)) {
2943 FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01002944 MCHBAR32(IOSAV_By_ERROR_COUNT(lane)) = 0;
2945 MCHBAR32(IOSAV_By_BW_SERROR_C(lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002946 }
Angel Pons88521882020-01-05 20:21:20 +01002947 wait_for_iosav(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002948
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002949 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02002950 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02002951 IOSAV_ACT, 1,
Angel Pons2be59002020-05-02 22:15:03 +02002952 4, 8, 40, SSQ_NA,
Angel Ponsca00dec2020-05-02 15:04:00 +02002953 0, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002954 0, 0, 1, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002955
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002956 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02002957 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02002958 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002959 100, 4, 40, SSQ_WR,
2960 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002961 0, 1, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002962
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002963 /* DRAM command RD */
Angel Ponsca00dec2020-05-02 15:04:00 +02002964 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02002965 IOSAV_RD, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002966 100, 4, 40, SSQ_RD,
2967 0, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002968 0, 1, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002969
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002970 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02002971 IOSAV_SUBSEQUENCE(channel, 3,
Angel Ponsb631d072020-05-02 20:00:32 +02002972 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02002973 1, 3, 40, SSQ_NA,
2974 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02002975 0, 0, 0, 0, 18, 0, 0, 0);
Felix Held9cf1dd22018-07-31 14:52:40 +02002976
Angel Pons7c49cb82020-03-16 23:17:32 +01002977 /* Execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02002978 iosav_run_once(channel);
Felix Held9cf1dd22018-07-31 14:52:40 +02002979
Angel Pons88521882020-01-05 20:21:20 +01002980 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002981 FOR_ALL_LANES
Angel Pons88521882020-01-05 20:21:20 +01002982 if (MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane))) {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01002983 printk(BIOS_EMERG, "Mini channel test failed (2): %d, %d, %d\n",
2984 channel, slotrank, lane);
2985 return MAKE_ERR;
2986 }
2987 }
2988 return 0;
2989}
2990
Patrick Rudolphdd662872017-10-28 18:20:11 +02002991void channel_scrub(ramctr_timing *ctrl)
2992{
2993 int channel, slotrank, row, rowsize;
2994
2995 FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
2996 rowsize = 1 << ctrl->info.dimm[channel][slotrank >> 1].row_bits;
2997 for (row = 0; row < rowsize; row += 16) {
2998
2999 wait_for_iosav(channel);
3000
3001 /* DRAM command ACT */
Angel Ponsca00dec2020-05-02 15:04:00 +02003002 IOSAV_SUBSEQUENCE(channel, 0,
Angel Ponsb631d072020-05-02 20:00:32 +02003003 IOSAV_ACT, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02003004 1, MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD), ctrl->tRCD, SSQ_NA,
3005 row, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02003006 1, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003007
3008 /* DRAM command WR */
Angel Ponsca00dec2020-05-02 15:04:00 +02003009 IOSAV_SUBSEQUENCE(channel, 1,
Angel Ponsb631d072020-05-02 20:00:32 +02003010 IOSAV_WR, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02003011 129, 4, 40, SSQ_WR,
3012 row, 0, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02003013 0, 1, 0, 0, 18, 0, 0, 0);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003014
3015 /* DRAM command PRE */
Angel Ponsca00dec2020-05-02 15:04:00 +02003016 IOSAV_SUBSEQUENCE(channel, 2,
Angel Ponsb631d072020-05-02 20:00:32 +02003017 IOSAV_PRE, 1,
Angel Ponsca00dec2020-05-02 15:04:00 +02003018 1, 3, 40, SSQ_NA,
3019 1024, 6, 0, slotrank,
Angel Ponsb631d072020-05-02 20:00:32 +02003020 0, 0, 0, 0, 18, 0, 0, 0);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003021
3022 /* execute command queue */
Angel Pons38d901e2020-05-02 23:50:43 +02003023 iosav_run_once(channel);
Patrick Rudolphdd662872017-10-28 18:20:11 +02003024
3025 wait_for_iosav(channel);
3026 }
3027 }
3028}
3029
Angel Pons88521882020-01-05 20:21:20 +01003030void set_scrambling_seed(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003031{
3032 int channel;
3033
Angel Pons7c49cb82020-03-16 23:17:32 +01003034 /* 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 +01003035 static u32 seeds[NUM_CHANNELS][3] = {
3036 {0x00009a36, 0xbafcfdcf, 0x46d1ab68},
3037 {0x00028bfa, 0x53fe4b49, 0x19ed5483}
3038 };
3039 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003040 MCHBAR32(SCHED_CBIT_ch(channel)) &= ~0x10000000;
Angel Pons7c49cb82020-03-16 23:17:32 +01003041 MCHBAR32(SCRAMBLING_SEED_1_ch(channel)) = seeds[channel][0];
3042 MCHBAR32(SCRAMBLING_SEED_2_HI_ch(channel)) = seeds[channel][1];
3043 MCHBAR32(SCRAMBLING_SEED_2_LO_ch(channel)) = seeds[channel][2];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003044 }
3045}
3046
Angel Pons89ae6b82020-03-21 13:23:32 +01003047void set_wmm_behavior(const u32 cpu)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003048{
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003049 if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003050 MCHBAR32(SC_WDBWM) = 0x141d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003051 } else {
Angel Pons7c49cb82020-03-16 23:17:32 +01003052 MCHBAR32(SC_WDBWM) = 0x551d1519;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003053 }
3054}
3055
Angel Pons88521882020-01-05 20:21:20 +01003056void prepare_training(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003057{
3058 int channel;
3059
3060 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003061 /* Always drive command bus */
Angel Pons88521882020-01-05 20:21:20 +01003062 MCHBAR32_OR(TC_RAP_ch(channel), 0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003063 }
3064
3065 udelay(1);
3066
3067 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003068 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003069 }
3070}
3071
Angel Pons7c49cb82020-03-16 23:17:32 +01003072void set_read_write_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003073{
3074 int channel, slotrank;
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003075
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003076 FOR_ALL_POPULATED_CHANNELS {
3077 u32 b20, b4_8_12;
Angel Pons88521882020-01-05 20:21:20 +01003078 int min_pi = 10000;
3079 int max_pi = -10000;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003080
3081 FOR_ALL_POPULATED_RANKS {
Angel Pons88521882020-01-05 20:21:20 +01003082 max_pi = MAX(ctrl->timings[channel][slotrank].pi_coding, max_pi);
3083 min_pi = MIN(ctrl->timings[channel][slotrank].pi_coding, min_pi);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003084 }
3085
Angel Pons7c49cb82020-03-16 23:17:32 +01003086 b20 = (max_pi - min_pi > 51) ? 0 : ctrl->ref_card_offset[channel];
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003087
Angel Pons7c49cb82020-03-16 23:17:32 +01003088 b4_8_12 = (ctrl->pi_coding_threshold < max_pi - min_pi) ? 0x3330 : 0x2220;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003089
Patrick Rudolph19c3dad2016-11-26 11:37:45 +01003090 dram_odt_stretch(ctrl, channel);
3091
Angel Pons7c49cb82020-03-16 23:17:32 +01003092 MCHBAR32(TC_RWP_ch(channel)) = 0x0a000000 | (b20 << 20) |
Felix Held2463aa92018-07-29 21:37:55 +02003093 ((ctrl->ref_card_offset[channel] + 2) << 16) | b4_8_12;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003094 }
3095}
3096
Angel Pons88521882020-01-05 20:21:20 +01003097void set_normal_operation(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003098{
3099 int channel;
3100 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003101 MCHBAR32(MC_INIT_STATE_ch(channel)) = 0x00001000 | ctrl->rankmap[channel];
3102 MCHBAR32_AND(TC_RAP_ch(channel), ~0x20000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003103 }
3104}
3105
Angel Pons7c49cb82020-03-16 23:17:32 +01003106/* Encode the watermark latencies in a suitable format for graphics drivers consumption */
3107static int encode_wm(int ns)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003108{
Angel Pons88521882020-01-05 20:21:20 +01003109 return (ns + 499) / 500;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003110}
3111
Angel Pons7c49cb82020-03-16 23:17:32 +01003112/* FIXME: values in this function should be hardware revision-dependent */
Angel Pons88521882020-01-05 20:21:20 +01003113void final_registers(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003114{
Patrick Rudolph74203de2017-11-20 11:57:01 +01003115 const size_t is_mobile = get_platform_type() == PLATFORM_MOBILE;
3116
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003117 int channel;
3118 int t1_cycles = 0, t1_ns = 0, t2_ns;
3119 int t3_ns;
3120 u32 r32;
3121
Angel Pons7c49cb82020-03-16 23:17:32 +01003122 /* FIXME: This register only exists on Ivy Bridge */
3123 MCHBAR32(WMM_READ_CONFIG) = 0x46;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003124
Felix Heldf9b826a2018-07-30 17:56:52 +02003125 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003126 MCHBAR32_AND_OR(TC_OTHP_ch(channel), 0xffffcfff, 0x1000);
Patrick Rudolph652c4912017-10-31 11:36:55 +01003127
Patrick Rudolph74203de2017-11-20 11:57:01 +01003128 if (is_mobile)
Patrick Rudolph652c4912017-10-31 11:36:55 +01003129 /* APD - DLL Off, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003130 MCHBAR32(PM_PDWN_CONFIG) = 0x00000740;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003131 else
Angel Pons7c49cb82020-03-16 23:17:32 +01003132 /* APD - PPD, 64 DCLKs until idle, decision per rank */
Angel Pons2a9a49b2019-12-31 14:24:12 +01003133 MCHBAR32(PM_PDWN_CONFIG) = 0x00000340;
Patrick Rudolph652c4912017-10-31 11:36:55 +01003134
Felix Heldf9b826a2018-07-30 17:56:52 +02003135 FOR_ALL_CHANNELS
Angel Pons88521882020-01-05 20:21:20 +01003136 MCHBAR32(PM_TRML_M_CONFIG_ch(channel)) = 0x00000aaa;
Felix Heldf9b826a2018-07-30 17:56:52 +02003137
Angel Pons88521882020-01-05 20:21:20 +01003138 MCHBAR32(PM_BW_LIMIT_CONFIG) = 0x5f7003ff; // OK
3139 MCHBAR32(PM_DLL_CONFIG) = 0x00073000 | ctrl->mdll_wake_delay; // OK
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003140
3141 FOR_ALL_CHANNELS {
3142 switch (ctrl->rankmap[channel]) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003143 /* Unpopulated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003144 case 0:
Angel Pons88521882020-01-05 20:21:20 +01003145 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003146 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003147 /* Only single-ranked dimms */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003148 case 1:
3149 case 4:
3150 case 5:
Angel Pons7c49cb82020-03-16 23:17:32 +01003151 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x00373131;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003152 break;
Angel Pons7c49cb82020-03-16 23:17:32 +01003153 /* Dual-ranked dimms present */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003154 default:
Angel Pons7c49cb82020-03-16 23:17:32 +01003155 MCHBAR32(PM_CMD_PWR_ch(channel)) = 0x009b6ea1;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003156 break;
3157 }
3158 }
3159
Felix Held50b7ed22019-12-30 20:41:54 +01003160 MCHBAR32(MEM_TRML_ESTIMATION_CONFIG) = 0xca9171e5;
Angel Pons7c49cb82020-03-16 23:17:32 +01003161 MCHBAR32_AND_OR(MEM_TRML_THRESHOLDS_CONFIG, ~0x00ffffff, 0x00e4d5d0);
Felix Held50b7ed22019-12-30 20:41:54 +01003162 MCHBAR32_AND(MEM_TRML_INTERRUPT, ~0x1f);
Felix Heldf9b826a2018-07-30 17:56:52 +02003163
3164 FOR_ALL_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003165 MCHBAR32_AND_OR(TC_RFP_ch(channel), ~(3 << 16), 1 << 16);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003166
Angel Pons88521882020-01-05 20:21:20 +01003167 MCHBAR32_OR(MC_INIT_STATE_G, 1);
3168 MCHBAR32_OR(MC_INIT_STATE_G, 0x80);
3169 MCHBAR32(BANDTIMERS_SNB) = 0xfa;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003170
Angel Pons7c49cb82020-03-16 23:17:32 +01003171 /* Find a populated channel */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003172 FOR_ALL_POPULATED_CHANNELS
3173 break;
3174
Angel Pons88521882020-01-05 20:21:20 +01003175 t1_cycles = (MCHBAR32(TC_ZQCAL_ch(channel)) >> 8) & 0xff;
3176 r32 = MCHBAR32(PM_DLL_CONFIG);
Angel Pons7c49cb82020-03-16 23:17:32 +01003177 if (r32 & (1 << 17))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003178 t1_cycles += (r32 & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003179 t1_cycles += MCHBAR32(TC_SRFTP_ch(channel)) & 0xfff;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003180 t1_ns = t1_cycles * ctrl->tCK / 256 + 544;
Angel Pons7c49cb82020-03-16 23:17:32 +01003181 if (!(r32 & (1 << 17)))
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003182 t1_ns += 500;
3183
Angel Pons88521882020-01-05 20:21:20 +01003184 t2_ns = 10 * ((MCHBAR32(SAPMTIMERS) >> 8) & 0xfff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003185 if (MCHBAR32(SAPMCTL) & 8) {
Angel Pons7c49cb82020-03-16 23:17:32 +01003186 t3_ns = 10 * ((MCHBAR32(BANDTIMERS_IVB) >> 8) & 0xfff);
Angel Pons88521882020-01-05 20:21:20 +01003187 t3_ns += 10 * (MCHBAR32(SAPMTIMERS2_IVB) & 0xff);
Angel Pons891f2bc2020-01-10 01:27:28 +01003188 } else {
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003189 t3_ns = 500;
3190 }
Angel Pons7c49cb82020-03-16 23:17:32 +01003191
3192 /* The graphics driver will use these watermark values */
3193 printk(BIOS_DEBUG, "t123: %d, %d, %d\n", t1_ns, t2_ns, t3_ns);
3194 MCHBAR32_AND_OR(SSKPD, 0xC0C0C0C0,
3195 ((encode_wm(t1_ns) + encode_wm(t2_ns)) << 16) | (encode_wm(t1_ns) << 8) |
3196 ((encode_wm(t3_ns) + encode_wm(t2_ns) + encode_wm(t1_ns)) << 24) | 0x0c);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003197}
3198
Angel Pons88521882020-01-05 20:21:20 +01003199void restore_timings(ramctr_timing *ctrl)
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003200{
3201 int channel, slotrank, lane;
3202
Angel Pons7c49cb82020-03-16 23:17:32 +01003203 FOR_ALL_POPULATED_CHANNELS {
3204 MCHBAR32(TC_RAP_ch(channel)) =
3205 (ctrl->tRRD << 0)
3206 | (ctrl->tRTP << 4)
3207 | (ctrl->tCKE << 8)
3208 | (ctrl->tWTR << 12)
3209 | (ctrl->tFAW << 16)
3210 | (ctrl->tWR << 24)
3211 | (ctrl->cmd_stretch[channel] << 30);
3212 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003213
3214 udelay(1);
3215
3216 FOR_ALL_POPULATED_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003217 wait_for_iosav(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003218 }
3219
3220 FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES {
Angel Pons88521882020-01-05 20:21:20 +01003221 MCHBAR32(IOSAV_By_BW_MASK_ch(channel, lane)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003222 }
3223
3224 FOR_ALL_POPULATED_CHANNELS
Angel Pons7c49cb82020-03-16 23:17:32 +01003225 MCHBAR32_OR(TC_RWP_ch(channel), 0x08000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003226
3227 FOR_ALL_POPULATED_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003228 udelay(1);
3229 MCHBAR32_OR(SCHED_CBIT_ch(channel), 0x00200000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003230 }
3231
3232 printram("CPE\n");
3233
Angel Pons88521882020-01-05 20:21:20 +01003234 MCHBAR32(GDCRTRAININGMOD) = 0;
3235 MCHBAR32(IOSAV_DC_MASK) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003236
3237 printram("CP5b\n");
3238
3239 FOR_ALL_POPULATED_CHANNELS {
3240 program_timings(ctrl, channel);
3241 }
3242
3243 u32 reg, addr;
3244
Angel Pons7c49cb82020-03-16 23:17:32 +01003245 /* Poll for RCOMP */
3246 while (!(MCHBAR32(RCOMP_TIMER) & (1 << 16)))
3247 ;
3248
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003249 do {
Angel Pons88521882020-01-05 20:21:20 +01003250 reg = MCHBAR32(IOSAV_STATUS_ch(0));
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003251 } while ((reg & 0x14) == 0);
3252
Angel Pons7c49cb82020-03-16 23:17:32 +01003253 /* Set state of memory controller */
Angel Pons88521882020-01-05 20:21:20 +01003254 MCHBAR32(MC_INIT_STATE_G) = 0x116;
Angel Pons7c49cb82020-03-16 23:17:32 +01003255 MCHBAR32(MC_INIT_STATE) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003256
Angel Pons7c49cb82020-03-16 23:17:32 +01003257 /* Wait 500us */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003258 udelay(500);
3259
3260 FOR_ALL_CHANNELS {
Angel Pons7c49cb82020-03-16 23:17:32 +01003261 /* Set valid rank CKE */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003262 reg = 0;
Angel Pons7c49cb82020-03-16 23:17:32 +01003263 reg = (reg & ~0x0f) | ctrl->rankmap[channel];
Angel Pons88521882020-01-05 20:21:20 +01003264 addr = MC_INIT_STATE_ch(channel);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003265 MCHBAR32(addr) = reg;
3266
Angel Pons7c49cb82020-03-16 23:17:32 +01003267 /* Wait 10ns for ranks to settle */
3268 // udelay(0.01);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003269
3270 reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4);
3271 MCHBAR32(addr) = reg;
3272
Angel Pons7c49cb82020-03-16 23:17:32 +01003273 /* Write reset using a NOP */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003274 write_reset(ctrl);
3275 }
3276
Angel Pons7c49cb82020-03-16 23:17:32 +01003277 /* MRS commands */
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003278 dram_mrscommands(ctrl);
3279
3280 printram("CP5c\n");
3281
Angel Pons88521882020-01-05 20:21:20 +01003282 MCHBAR32(GDCRTRAININGMOD_ch(0)) = 0;
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003283
3284 FOR_ALL_CHANNELS {
Angel Pons88521882020-01-05 20:21:20 +01003285 MCHBAR32_AND(GDCRCMDDEBUGMUXCFG_Cz_S(channel), ~0x3f000000);
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003286 udelay(2);
3287 }
Patrick Rudolphfd5fa2a2016-11-11 18:22:33 +01003288}