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