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