blob: 82481abe30acc99977dbbb3b2f0ae2a3bb5e8357 [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>
Arthur Heymans6d7a8c12017-03-07 20:48:14 +01006#include "x4x.h"
7
8#define MAX_COARSE 15
9#define DQS_HIGH 1
10#define DQS_LOW 0
11
12#define RESET_CNTL(channel) (0x5d8 + channel * 0x400)
13
14struct rec_timing {
15 u8 medium;
16 u8 coarse;
17 u8 pi;
18 u8 tap;
19};
20
Angel Pons879c4de2020-07-24 16:15:04 +020021static inline void mfence(void)
Arthur Heymans6d7a8c12017-03-07 20:48:14 +010022{
23 asm volatile("mfence":::);
24}
25
26static u8 sampledqs(u32 addr, u8 lane, u8 channel)
27{
Arthur Heymans6d7a8c12017-03-07 20:48:14 +010028 u32 sample_offset = 0x400 * channel + 0x561 + lane * 4;
29
30 /* Reset the DQS probe */
31 MCHBAR8(RESET_CNTL(channel)) &= ~0x2;
32 udelay(2);
33 MCHBAR8(RESET_CNTL(channel)) |= 0x2;
34 udelay(2);
Angel Pons879c4de2020-07-24 16:15:04 +020035 mfence();
Elyes HAOUAS2dbc0952019-05-22 21:44:48 +020036 /* Read strobe */
37 read32((u32 *)addr);
Angel Pons879c4de2020-07-24 16:15:04 +020038 mfence();
Arthur Heymans6d7a8c12017-03-07 20:48:14 +010039 return (MCHBAR8(sample_offset) >> 6) & 1;
40}
41
42static void program_timing(const struct rec_timing *timing, u8 channel,
43 u8 lane)
44{
45 u32 reg32;
46 u16 reg16;
47 u8 reg8;
48
49 printk(RAM_SPEW, " Programming timings:"
50 "Coarse: %d, Medium: %d, TAP: %d, PI: %d\n",
51 timing->coarse, timing->medium, timing->tap, timing->pi);
52
53 reg32 = MCHBAR32(0x400 * channel + 0x248);
54 reg32 &= ~0xf0000;
55 reg32 |= timing->coarse << 16;
56 MCHBAR32(0x400 * channel + 0x248) = reg32;
57
58 reg16 = MCHBAR16(0x400 * channel + 0x58c);
59 reg16 &= ~(3 << (lane * 2));
60 reg16 |= timing->medium << (lane * 2);
61 MCHBAR16(0x400 * channel + 0x58c) = reg16;
62
63 reg8 = MCHBAR8(0x400 * channel + 0x560 + lane * 4);
64 reg8 &= ~0x7f;
65 reg8 |= timing->tap;
66 reg8 |= timing->pi << 4;
67 MCHBAR8(0x400 * channel + 0x560 + lane * 4) = reg8;
68}
69
70static int increase_medium(struct rec_timing *timing)
71{
72 if (timing->medium < 3) {
73 timing->medium++;
74 } else if (timing->coarse < MAX_COARSE) {
75 timing->medium = 0;
76 timing->coarse++;
77 } else {
78 printk(BIOS_ERR, "Cannot increase medium any further.\n");
79 return -1;
80 }
81 return 0;
82}
83
84static int decrease_medium(struct rec_timing *timing)
85{
86 if (timing->medium > 0) {
87 timing->medium--;
88 } else if (timing->coarse > 0) {
89 timing->medium = 3;
90 timing->coarse--;
91 } else {
92 printk(BIOS_ERR, "Cannot lower medium any further.\n");
93 return -1;
94 }
95 return 0;
96}
97
98static int increase_tap(struct rec_timing *timing)
99{
100 if (timing->tap == 14) {
101 if (increase_medium(timing))
102 return -1;
103 timing->tap = 0;
104 } else {
105 timing->tap++;
106 }
107 return 0;
108}
109
110static int decrease_tap(struct rec_timing *timing)
111{
112 if (timing->tap > 0) {
113 timing->tap--;
114 } else {
115 if (decrease_medium(timing))
116 return -1;
117 timing->tap = 14;
118 }
119 return 0;
120}
121
122static int decr_coarse_low(u8 channel, u8 lane, u32 addr,
123 struct rec_timing *timing)
124{
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100125 printk(RAM_DEBUG,
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100126 " Decreasing coarse until high to low transition is found\n");
127 while (sampledqs(addr, lane, channel) != DQS_LOW) {
128 if (timing->coarse == 0) {
129 printk(BIOS_CRIT,
130 "Couldn't find DQS-high 0 indicator, halt\n");
131 return -1;
132 }
133 timing->coarse--;
134 program_timing(timing, channel, lane);
135 }
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100136 printk(RAM_DEBUG, " DQS low at coarse=%d medium=%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100137 timing->coarse, timing->medium);
138 return 0;
139}
140
141static int fine_search_dqs_high(u8 channel, u8 lane, u32 addr,
142 struct rec_timing *timing)
143{
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100144 printk(RAM_DEBUG,
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100145 " Increasing TAP until low to high transition is found\n");
146 /*
147 * We use a do while loop since it happens that the strobe read
148 * is inconsistent, with the strobe already high. The current
149 * code flow results in failure later when finding the preamble,
150 * at which DQS needs to be high is often not the case if TAP was
151 * not increased at least once here. Work around this by incrementing
152 * TAP at least once to guarantee searching for preamble start at
153 * DQS high.
154 * This seems to be the result of hysteresis on some settings, where
155 * the DQS probe is influenced by its previous value.
156 */
157 if (sampledqs(addr, lane, channel) == DQS_HIGH) {
158 printk(BIOS_WARNING,
159 "DQS already HIGH... DQS probe is inconsistent!\n"
160 "Continuing....\n");
161 }
162 do {
163 if (increase_tap(timing)) {
164 printk(BIOS_CRIT,
165 "Could not find DQS-high on fine search.\n");
166 return -1;
167 }
168 program_timing(timing, channel, lane);
169 } while (sampledqs(addr, lane, channel) != DQS_HIGH);
170
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100171 printk(RAM_DEBUG, " DQS high at coarse=%d medium=%d tap:%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100172 timing->coarse, timing->medium, timing->tap);
173 return 0;
174}
175
176static int find_dqs_low(u8 channel, u8 lane, u32 addr,
177 struct rec_timing *timing)
178{
179 /* Look for DQS low, using quarter steps. */
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100180 printk(RAM_DEBUG, " Increasing medium until DQS LOW is found\n");
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100181 while (sampledqs(addr, lane, channel) != DQS_LOW) {
182 if (increase_medium(timing)) {
183 printk(BIOS_CRIT,
184 "Coarse > 15: DQS tuning failed, halt\n");
185 return -1;
186 }
187 program_timing(timing, channel, lane);
188 }
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100189 printk(RAM_DEBUG, " DQS low at coarse=%d medium=%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100190 timing->coarse, timing->medium);
191 return 0;
192}
193static int find_dqs_high(u8 channel, u8 lane, u32 addr,
194 struct rec_timing *timing)
195{
196 /* Look for DQS high, using quarter steps. */
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100197 printk(RAM_DEBUG, " Increasing medium until DQS HIGH is found\n");
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100198 while (sampledqs(addr, lane, channel) != DQS_HIGH) {
199 if (increase_medium(timing)) {
200 printk(BIOS_CRIT,
201 "Coarse > 16: DQS tuning failed, halt\n");
202 return -1;
203 }
204 program_timing(timing, channel, lane);
205 }
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100206 printk(RAM_DEBUG, " DQS high at coarse=%d medium=%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100207 timing->coarse, timing->medium);
208 return 0;
209}
210
211static int find_dqs_edge_lowhigh(u8 channel, u8 lane,
212 u32 addr, struct rec_timing *timing)
213{
214 /* Medium search for DQS high. */
215 if (find_dqs_high(channel, lane, addr, timing))
216 return -1;
217
218 /* Go back and perform finer search. */
219 if (decrease_medium(timing))
220 return -1;
221 program_timing(timing, channel, lane);
222 if (fine_search_dqs_high(channel, lane, addr, timing) < 0)
223 return -1;
224
225 return 0;
226}
227
228static int find_preamble(u8 channel, u8 lane, u32 addr,
229 struct rec_timing *timing)
230{
231 /* Add a quarter step */
232 if (increase_medium(timing))
233 return -1;
234 program_timing(timing, channel, lane);
235 /* Verify we are at high */
236 if (sampledqs(addr, lane, channel) != DQS_HIGH) {
237 printk(BIOS_CRIT, "Not at DQS high, d'oh\n");
238 return -1;
239 }
240
241 /* Decrease coarse until LOW is found */
242 if (decr_coarse_low(channel, lane, addr, timing))
243 return -1;
244 return 0;
245}
246
247static int calibrate_receive_enable(u8 channel, u8 lane,
248 u32 addr, struct rec_timing *timing)
249{
250 program_timing(timing, channel, lane);
251 /* Set receive enable bit */
252 MCHBAR16(0x400 * channel + 0x588) = (MCHBAR16(0x400 * channel + 0x588)
253 & ~(3 << (lane * 2))) | (1 << (lane * 2));
254
255 if (find_dqs_low(channel, lane, addr, timing))
256 return -1;
257
258 /* Advance a little further. */
259 if (increase_medium(timing)) {
260 /* A finer search could be implemented */
261 printk(BIOS_WARNING, "Cannot increase medium further");
262 return -1;
263 }
264 program_timing(timing, channel, lane);
265
266 if (find_dqs_edge_lowhigh(channel, lane, addr, timing))
267 return -1;
268
269 /* Go back on fine search */
270 if (decrease_tap(timing))
271 return -1;
272 timing->pi = 3;
273 program_timing(timing, channel, lane);
274
275 if (find_preamble(channel, lane, addr, timing))
276 return -1;
277
278 if (find_dqs_edge_lowhigh(channel, lane, addr, timing))
279 return -1;
280 if (decrease_tap(timing))
281 return -1;
282 timing->pi = 7;
283 program_timing(timing, channel, lane);
284
285 /* Unset receive enable bit */
286 MCHBAR16(0x400 * channel + 0x588) = MCHBAR16(0x400 * channel + 0x588) &
287 ~(3 << (lane * 2));
288 return 0;
289}
290
Arthur Heymansadc571a2017-09-25 09:40:54 +0200291void rcven(struct sysinfo *s)
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100292{
Arthur Heymans1994e4482017-11-04 07:52:23 +0100293 int rank;
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100294 u8 channel, lane, reg8;
Arthur Heymans1994e4482017-11-04 07:52:23 +0100295 /*
296 * Using the macros below the compiler warns about this possibly being
297 * unitialised.
298 */
299 u32 addr = 0;
Arthur Heymans276049f2017-11-05 05:56:34 +0100300 struct rec_timing timing[TOTAL_BYTELANES];
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100301 u8 mincoarse;
302
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100303 printk(BIOS_DEBUG, "Starting DQS receiver enable calibration\n");
304
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100305 MCHBAR8(0x5d8) = MCHBAR8(0x5d8) & ~0xc;
306 MCHBAR8(0x9d8) = MCHBAR8(0x9d8) & ~0xc;
307 MCHBAR8(0x5dc) = MCHBAR8(0x5dc) & ~0x80;
308 FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) {
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100309 mincoarse = 0xff;
Arthur Heymans1994e4482017-11-04 07:52:23 +0100310 /*
311 * Receive enable calibration happens on the first populated
312 * rank on each channel.
313 */
314 FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, channel, rank) {
315 addr = test_address(channel, rank);
316 break;
317 }
Arthur Heymans276049f2017-11-05 05:56:34 +0100318 FOR_EACH_BYTELANE(lane) {
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100319 printk(BIOS_DEBUG, "Channel %d, Lane %d addr=0x%08x\n",
320 channel, lane, addr);
321 timing[lane].coarse = (s->selected_timings.CAS + 1);
322 switch (lane) {
323 default:
324 case 0:
325 case 1:
326 timing[lane].medium = 0;
327 break;
328 case 2:
329 case 3:
330 timing[lane].medium = 1;
331 break;
332 case 4:
333 case 5:
334 timing[lane].medium = 2;
335 break;
336 case 6:
337 case 7:
338 timing[lane].medium = 3;
339 break;
340 }
341 timing[lane].tap = 0;
342 timing[lane].pi = 0;
343
344 if (calibrate_receive_enable(channel, lane, addr,
345 &timing[lane]))
346 die("Receive enable calibration failed\n");
347 if (mincoarse > timing[lane].coarse)
348 mincoarse = timing[lane].coarse;
349 }
350 printk(BIOS_DEBUG, "Found min coarse value = %d\n", mincoarse);
Arthur Heymansadc571a2017-09-25 09:40:54 +0200351 s->rcven_t[channel].min_common_coarse = mincoarse;
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100352 printk(BIOS_DEBUG, "Receive enable, final timings:\n");
353 /* Normalise coarse */
Arthur Heymans276049f2017-11-05 05:56:34 +0100354 FOR_EACH_BYTELANE(lane) {
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100355 if (timing[lane].coarse == 0)
356 reg8 = 0;
357 else
358 reg8 = timing[lane].coarse - mincoarse;
359 printk(BIOS_DEBUG, "ch %d lane %d: coarse offset: %d;"
360 "medium: %d; tap: %d\n",
361 channel, lane, reg8, timing[lane].medium,
362 timing[lane].tap);
Arthur Heymansadc571a2017-09-25 09:40:54 +0200363 s->rcven_t[channel].coarse_offset[lane] = reg8;
364 s->rcven_t[channel].medium[lane] = timing[lane].medium;
365 s->rcven_t[channel].tap[lane] = timing[lane].tap;
366 s->rcven_t[channel].pi[lane] = timing[lane].pi;
Arthur Heymansf6f4ba92017-12-11 07:36:15 +0100367 MCHBAR16(0x400 * channel + 0x5fa) =
368 (MCHBAR16(0x400 * channel + 0x5fa) &
369 ~(3 << (lane * 2))) | (reg8 << (lane * 2));
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100370 }
371 /* simply use timing[0] to program mincoarse */
372 timing[0].coarse = mincoarse;
373 program_timing(&timing[0], channel, 0);
374 }
375}