blob: 3b59df2038b47c8bb3ba1236a618137970b33323 [file] [log] [blame]
Patrick Georgi02363b52020-05-05 20:48:50 +02001/* This file is part of the coreboot project. */
Patrick Georgiac959032020-05-05 22:49:26 +02002/* SPDX-License-Identifier: GPL-2.0-or-later */
Arthur Heymans6d7a8c12017-03-07 20:48:14 +01003
Kyösti Mälkki13f66502019-03-03 08:01:05 +02004#include <device/mmio.h>
Arthur Heymans6d7a8c12017-03-07 20:48:14 +01005#include <console/console.h>
6#include <delay.h>
7#include "iomap.h"
8#include "x4x.h"
9
10#define MAX_COARSE 15
11#define DQS_HIGH 1
12#define DQS_LOW 0
13
14#define RESET_CNTL(channel) (0x5d8 + channel * 0x400)
15
16struct rec_timing {
17 u8 medium;
18 u8 coarse;
19 u8 pi;
20 u8 tap;
21};
22
23static inline void barrier(void)
24{
25 asm volatile("mfence":::);
26}
27
28static u8 sampledqs(u32 addr, u8 lane, u8 channel)
29{
Arthur Heymans6d7a8c12017-03-07 20:48:14 +010030 u32 sample_offset = 0x400 * channel + 0x561 + lane * 4;
31
32 /* Reset the DQS probe */
33 MCHBAR8(RESET_CNTL(channel)) &= ~0x2;
34 udelay(2);
35 MCHBAR8(RESET_CNTL(channel)) |= 0x2;
36 udelay(2);
37 barrier();
Elyes HAOUAS2dbc0952019-05-22 21:44:48 +020038 /* Read strobe */
39 read32((u32 *)addr);
Arthur Heymans6d7a8c12017-03-07 20:48:14 +010040 barrier();
41 return (MCHBAR8(sample_offset) >> 6) & 1;
42}
43
44static void program_timing(const struct rec_timing *timing, u8 channel,
45 u8 lane)
46{
47 u32 reg32;
48 u16 reg16;
49 u8 reg8;
50
51 printk(RAM_SPEW, " Programming timings:"
52 "Coarse: %d, Medium: %d, TAP: %d, PI: %d\n",
53 timing->coarse, timing->medium, timing->tap, timing->pi);
54
55 reg32 = MCHBAR32(0x400 * channel + 0x248);
56 reg32 &= ~0xf0000;
57 reg32 |= timing->coarse << 16;
58 MCHBAR32(0x400 * channel + 0x248) = reg32;
59
60 reg16 = MCHBAR16(0x400 * channel + 0x58c);
61 reg16 &= ~(3 << (lane * 2));
62 reg16 |= timing->medium << (lane * 2);
63 MCHBAR16(0x400 * channel + 0x58c) = reg16;
64
65 reg8 = MCHBAR8(0x400 * channel + 0x560 + lane * 4);
66 reg8 &= ~0x7f;
67 reg8 |= timing->tap;
68 reg8 |= timing->pi << 4;
69 MCHBAR8(0x400 * channel + 0x560 + lane * 4) = reg8;
70}
71
72static int increase_medium(struct rec_timing *timing)
73{
74 if (timing->medium < 3) {
75 timing->medium++;
76 } else if (timing->coarse < MAX_COARSE) {
77 timing->medium = 0;
78 timing->coarse++;
79 } else {
80 printk(BIOS_ERR, "Cannot increase medium any further.\n");
81 return -1;
82 }
83 return 0;
84}
85
86static int decrease_medium(struct rec_timing *timing)
87{
88 if (timing->medium > 0) {
89 timing->medium--;
90 } else if (timing->coarse > 0) {
91 timing->medium = 3;
92 timing->coarse--;
93 } else {
94 printk(BIOS_ERR, "Cannot lower medium any further.\n");
95 return -1;
96 }
97 return 0;
98}
99
100static int increase_tap(struct rec_timing *timing)
101{
102 if (timing->tap == 14) {
103 if (increase_medium(timing))
104 return -1;
105 timing->tap = 0;
106 } else {
107 timing->tap++;
108 }
109 return 0;
110}
111
112static int decrease_tap(struct rec_timing *timing)
113{
114 if (timing->tap > 0) {
115 timing->tap--;
116 } else {
117 if (decrease_medium(timing))
118 return -1;
119 timing->tap = 14;
120 }
121 return 0;
122}
123
124static int decr_coarse_low(u8 channel, u8 lane, u32 addr,
125 struct rec_timing *timing)
126{
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100127 printk(RAM_DEBUG,
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100128 " Decreasing coarse until high to low transition is found\n");
129 while (sampledqs(addr, lane, channel) != DQS_LOW) {
130 if (timing->coarse == 0) {
131 printk(BIOS_CRIT,
132 "Couldn't find DQS-high 0 indicator, halt\n");
133 return -1;
134 }
135 timing->coarse--;
136 program_timing(timing, channel, lane);
137 }
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100138 printk(RAM_DEBUG, " DQS low at coarse=%d medium=%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100139 timing->coarse, timing->medium);
140 return 0;
141}
142
143static int fine_search_dqs_high(u8 channel, u8 lane, u32 addr,
144 struct rec_timing *timing)
145{
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100146 printk(RAM_DEBUG,
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100147 " Increasing TAP until low to high transition is found\n");
148 /*
149 * We use a do while loop since it happens that the strobe read
150 * is inconsistent, with the strobe already high. The current
151 * code flow results in failure later when finding the preamble,
152 * at which DQS needs to be high is often not the case if TAP was
153 * not increased at least once here. Work around this by incrementing
154 * TAP at least once to guarantee searching for preamble start at
155 * DQS high.
156 * This seems to be the result of hysteresis on some settings, where
157 * the DQS probe is influenced by its previous value.
158 */
159 if (sampledqs(addr, lane, channel) == DQS_HIGH) {
160 printk(BIOS_WARNING,
161 "DQS already HIGH... DQS probe is inconsistent!\n"
162 "Continuing....\n");
163 }
164 do {
165 if (increase_tap(timing)) {
166 printk(BIOS_CRIT,
167 "Could not find DQS-high on fine search.\n");
168 return -1;
169 }
170 program_timing(timing, channel, lane);
171 } while (sampledqs(addr, lane, channel) != DQS_HIGH);
172
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100173 printk(RAM_DEBUG, " DQS high at coarse=%d medium=%d tap:%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100174 timing->coarse, timing->medium, timing->tap);
175 return 0;
176}
177
178static int find_dqs_low(u8 channel, u8 lane, u32 addr,
179 struct rec_timing *timing)
180{
181 /* Look for DQS low, using quarter steps. */
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100182 printk(RAM_DEBUG, " Increasing medium until DQS LOW is found\n");
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100183 while (sampledqs(addr, lane, channel) != DQS_LOW) {
184 if (increase_medium(timing)) {
185 printk(BIOS_CRIT,
186 "Coarse > 15: DQS tuning failed, halt\n");
187 return -1;
188 }
189 program_timing(timing, channel, lane);
190 }
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100191 printk(RAM_DEBUG, " DQS low at coarse=%d medium=%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100192 timing->coarse, timing->medium);
193 return 0;
194}
195static int find_dqs_high(u8 channel, u8 lane, u32 addr,
196 struct rec_timing *timing)
197{
198 /* Look for DQS high, using quarter steps. */
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100199 printk(RAM_DEBUG, " Increasing medium until DQS HIGH is found\n");
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100200 while (sampledqs(addr, lane, channel) != DQS_HIGH) {
201 if (increase_medium(timing)) {
202 printk(BIOS_CRIT,
203 "Coarse > 16: DQS tuning failed, halt\n");
204 return -1;
205 }
206 program_timing(timing, channel, lane);
207 }
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100208 printk(RAM_DEBUG, " DQS high at coarse=%d medium=%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100209 timing->coarse, timing->medium);
210 return 0;
211}
212
213static int find_dqs_edge_lowhigh(u8 channel, u8 lane,
214 u32 addr, struct rec_timing *timing)
215{
216 /* Medium search for DQS high. */
217 if (find_dqs_high(channel, lane, addr, timing))
218 return -1;
219
220 /* Go back and perform finer search. */
221 if (decrease_medium(timing))
222 return -1;
223 program_timing(timing, channel, lane);
224 if (fine_search_dqs_high(channel, lane, addr, timing) < 0)
225 return -1;
226
227 return 0;
228}
229
230static int find_preamble(u8 channel, u8 lane, u32 addr,
231 struct rec_timing *timing)
232{
233 /* Add a quarter step */
234 if (increase_medium(timing))
235 return -1;
236 program_timing(timing, channel, lane);
237 /* Verify we are at high */
238 if (sampledqs(addr, lane, channel) != DQS_HIGH) {
239 printk(BIOS_CRIT, "Not at DQS high, d'oh\n");
240 return -1;
241 }
242
243 /* Decrease coarse until LOW is found */
244 if (decr_coarse_low(channel, lane, addr, timing))
245 return -1;
246 return 0;
247}
248
249static int calibrate_receive_enable(u8 channel, u8 lane,
250 u32 addr, struct rec_timing *timing)
251{
252 program_timing(timing, channel, lane);
253 /* Set receive enable bit */
254 MCHBAR16(0x400 * channel + 0x588) = (MCHBAR16(0x400 * channel + 0x588)
255 & ~(3 << (lane * 2))) | (1 << (lane * 2));
256
257 if (find_dqs_low(channel, lane, addr, timing))
258 return -1;
259
260 /* Advance a little further. */
261 if (increase_medium(timing)) {
262 /* A finer search could be implemented */
263 printk(BIOS_WARNING, "Cannot increase medium further");
264 return -1;
265 }
266 program_timing(timing, channel, lane);
267
268 if (find_dqs_edge_lowhigh(channel, lane, addr, timing))
269 return -1;
270
271 /* Go back on fine search */
272 if (decrease_tap(timing))
273 return -1;
274 timing->pi = 3;
275 program_timing(timing, channel, lane);
276
277 if (find_preamble(channel, lane, addr, timing))
278 return -1;
279
280 if (find_dqs_edge_lowhigh(channel, lane, addr, timing))
281 return -1;
282 if (decrease_tap(timing))
283 return -1;
284 timing->pi = 7;
285 program_timing(timing, channel, lane);
286
287 /* Unset receive enable bit */
288 MCHBAR16(0x400 * channel + 0x588) = MCHBAR16(0x400 * channel + 0x588) &
289 ~(3 << (lane * 2));
290 return 0;
291}
292
Arthur Heymansadc571a2017-09-25 09:40:54 +0200293void rcven(struct sysinfo *s)
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100294{
Arthur Heymans1994e4482017-11-04 07:52:23 +0100295 int rank;
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100296 u8 channel, lane, reg8;
Arthur Heymans1994e4482017-11-04 07:52:23 +0100297 /*
298 * Using the macros below the compiler warns about this possibly being
299 * unitialised.
300 */
301 u32 addr = 0;
Arthur Heymans276049f2017-11-05 05:56:34 +0100302 struct rec_timing timing[TOTAL_BYTELANES];
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100303 u8 mincoarse;
304
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100305 printk(BIOS_DEBUG, "Starting DQS receiver enable calibration\n");
306
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100307 MCHBAR8(0x5d8) = MCHBAR8(0x5d8) & ~0xc;
308 MCHBAR8(0x9d8) = MCHBAR8(0x9d8) & ~0xc;
309 MCHBAR8(0x5dc) = MCHBAR8(0x5dc) & ~0x80;
310 FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) {
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100311 mincoarse = 0xff;
Arthur Heymans1994e4482017-11-04 07:52:23 +0100312 /*
313 * Receive enable calibration happens on the first populated
314 * rank on each channel.
315 */
316 FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, channel, rank) {
317 addr = test_address(channel, rank);
318 break;
319 }
Arthur Heymans276049f2017-11-05 05:56:34 +0100320 FOR_EACH_BYTELANE(lane) {
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100321 printk(BIOS_DEBUG, "Channel %d, Lane %d addr=0x%08x\n",
322 channel, lane, addr);
323 timing[lane].coarse = (s->selected_timings.CAS + 1);
324 switch (lane) {
325 default:
326 case 0:
327 case 1:
328 timing[lane].medium = 0;
329 break;
330 case 2:
331 case 3:
332 timing[lane].medium = 1;
333 break;
334 case 4:
335 case 5:
336 timing[lane].medium = 2;
337 break;
338 case 6:
339 case 7:
340 timing[lane].medium = 3;
341 break;
342 }
343 timing[lane].tap = 0;
344 timing[lane].pi = 0;
345
346 if (calibrate_receive_enable(channel, lane, addr,
347 &timing[lane]))
348 die("Receive enable calibration failed\n");
349 if (mincoarse > timing[lane].coarse)
350 mincoarse = timing[lane].coarse;
351 }
352 printk(BIOS_DEBUG, "Found min coarse value = %d\n", mincoarse);
Arthur Heymansadc571a2017-09-25 09:40:54 +0200353 s->rcven_t[channel].min_common_coarse = mincoarse;
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100354 printk(BIOS_DEBUG, "Receive enable, final timings:\n");
355 /* Normalise coarse */
Arthur Heymans276049f2017-11-05 05:56:34 +0100356 FOR_EACH_BYTELANE(lane) {
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100357 if (timing[lane].coarse == 0)
358 reg8 = 0;
359 else
360 reg8 = timing[lane].coarse - mincoarse;
361 printk(BIOS_DEBUG, "ch %d lane %d: coarse offset: %d;"
362 "medium: %d; tap: %d\n",
363 channel, lane, reg8, timing[lane].medium,
364 timing[lane].tap);
Arthur Heymansadc571a2017-09-25 09:40:54 +0200365 s->rcven_t[channel].coarse_offset[lane] = reg8;
366 s->rcven_t[channel].medium[lane] = timing[lane].medium;
367 s->rcven_t[channel].tap[lane] = timing[lane].tap;
368 s->rcven_t[channel].pi[lane] = timing[lane].pi;
Arthur Heymansf6f4ba92017-12-11 07:36:15 +0100369 MCHBAR16(0x400 * channel + 0x5fa) =
370 (MCHBAR16(0x400 * channel + 0x5fa) &
371 ~(3 << (lane * 2))) | (reg8 << (lane * 2));
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100372 }
373 /* simply use timing[0] to program mincoarse */
374 timing[0].coarse = mincoarse;
375 program_timing(&timing[0], channel, 0);
376 }
377}