blob: ea5762188c39ae449026807fa6af7f339e373e5c [file] [log] [blame]
Arthur Heymans6d7a8c12017-03-07 20:48:14 +01001/*
2 * This file is part of the coreboot project.
3 *
Arthur Heymans6d7a8c12017-03-07 20:48:14 +01004 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
Kyösti Mälkki13f66502019-03-03 08:01:05 +020016#include <device/mmio.h>
Arthur Heymans6d7a8c12017-03-07 20:48:14 +010017#include <console/console.h>
18#include <delay.h>
19#include "iomap.h"
20#include "x4x.h"
21
22#define MAX_COARSE 15
23#define DQS_HIGH 1
24#define DQS_LOW 0
25
26#define RESET_CNTL(channel) (0x5d8 + channel * 0x400)
27
28struct rec_timing {
29 u8 medium;
30 u8 coarse;
31 u8 pi;
32 u8 tap;
33};
34
35static inline void barrier(void)
36{
37 asm volatile("mfence":::);
38}
39
40static u8 sampledqs(u32 addr, u8 lane, u8 channel)
41{
Arthur Heymans6d7a8c12017-03-07 20:48:14 +010042 u32 sample_offset = 0x400 * channel + 0x561 + lane * 4;
43
44 /* Reset the DQS probe */
45 MCHBAR8(RESET_CNTL(channel)) &= ~0x2;
46 udelay(2);
47 MCHBAR8(RESET_CNTL(channel)) |= 0x2;
48 udelay(2);
49 barrier();
Elyes HAOUAS2dbc0952019-05-22 21:44:48 +020050 /* Read strobe */
51 read32((u32 *)addr);
Arthur Heymans6d7a8c12017-03-07 20:48:14 +010052 barrier();
53 return (MCHBAR8(sample_offset) >> 6) & 1;
54}
55
56static void program_timing(const struct rec_timing *timing, u8 channel,
57 u8 lane)
58{
59 u32 reg32;
60 u16 reg16;
61 u8 reg8;
62
63 printk(RAM_SPEW, " Programming timings:"
64 "Coarse: %d, Medium: %d, TAP: %d, PI: %d\n",
65 timing->coarse, timing->medium, timing->tap, timing->pi);
66
67 reg32 = MCHBAR32(0x400 * channel + 0x248);
68 reg32 &= ~0xf0000;
69 reg32 |= timing->coarse << 16;
70 MCHBAR32(0x400 * channel + 0x248) = reg32;
71
72 reg16 = MCHBAR16(0x400 * channel + 0x58c);
73 reg16 &= ~(3 << (lane * 2));
74 reg16 |= timing->medium << (lane * 2);
75 MCHBAR16(0x400 * channel + 0x58c) = reg16;
76
77 reg8 = MCHBAR8(0x400 * channel + 0x560 + lane * 4);
78 reg8 &= ~0x7f;
79 reg8 |= timing->tap;
80 reg8 |= timing->pi << 4;
81 MCHBAR8(0x400 * channel + 0x560 + lane * 4) = reg8;
82}
83
84static int increase_medium(struct rec_timing *timing)
85{
86 if (timing->medium < 3) {
87 timing->medium++;
88 } else if (timing->coarse < MAX_COARSE) {
89 timing->medium = 0;
90 timing->coarse++;
91 } else {
92 printk(BIOS_ERR, "Cannot increase medium any further.\n");
93 return -1;
94 }
95 return 0;
96}
97
98static int decrease_medium(struct rec_timing *timing)
99{
100 if (timing->medium > 0) {
101 timing->medium--;
102 } else if (timing->coarse > 0) {
103 timing->medium = 3;
104 timing->coarse--;
105 } else {
106 printk(BIOS_ERR, "Cannot lower medium any further.\n");
107 return -1;
108 }
109 return 0;
110}
111
112static int increase_tap(struct rec_timing *timing)
113{
114 if (timing->tap == 14) {
115 if (increase_medium(timing))
116 return -1;
117 timing->tap = 0;
118 } else {
119 timing->tap++;
120 }
121 return 0;
122}
123
124static int decrease_tap(struct rec_timing *timing)
125{
126 if (timing->tap > 0) {
127 timing->tap--;
128 } else {
129 if (decrease_medium(timing))
130 return -1;
131 timing->tap = 14;
132 }
133 return 0;
134}
135
136static int decr_coarse_low(u8 channel, u8 lane, u32 addr,
137 struct rec_timing *timing)
138{
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100139 printk(RAM_DEBUG,
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100140 " Decreasing coarse until high to low transition is found\n");
141 while (sampledqs(addr, lane, channel) != DQS_LOW) {
142 if (timing->coarse == 0) {
143 printk(BIOS_CRIT,
144 "Couldn't find DQS-high 0 indicator, halt\n");
145 return -1;
146 }
147 timing->coarse--;
148 program_timing(timing, channel, lane);
149 }
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100150 printk(RAM_DEBUG, " DQS low at coarse=%d medium=%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100151 timing->coarse, timing->medium);
152 return 0;
153}
154
155static int fine_search_dqs_high(u8 channel, u8 lane, u32 addr,
156 struct rec_timing *timing)
157{
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100158 printk(RAM_DEBUG,
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100159 " Increasing TAP until low to high transition is found\n");
160 /*
161 * We use a do while loop since it happens that the strobe read
162 * is inconsistent, with the strobe already high. The current
163 * code flow results in failure later when finding the preamble,
164 * at which DQS needs to be high is often not the case if TAP was
165 * not increased at least once here. Work around this by incrementing
166 * TAP at least once to guarantee searching for preamble start at
167 * DQS high.
168 * This seems to be the result of hysteresis on some settings, where
169 * the DQS probe is influenced by its previous value.
170 */
171 if (sampledqs(addr, lane, channel) == DQS_HIGH) {
172 printk(BIOS_WARNING,
173 "DQS already HIGH... DQS probe is inconsistent!\n"
174 "Continuing....\n");
175 }
176 do {
177 if (increase_tap(timing)) {
178 printk(BIOS_CRIT,
179 "Could not find DQS-high on fine search.\n");
180 return -1;
181 }
182 program_timing(timing, channel, lane);
183 } while (sampledqs(addr, lane, channel) != DQS_HIGH);
184
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100185 printk(RAM_DEBUG, " DQS high at coarse=%d medium=%d tap:%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100186 timing->coarse, timing->medium, timing->tap);
187 return 0;
188}
189
190static int find_dqs_low(u8 channel, u8 lane, u32 addr,
191 struct rec_timing *timing)
192{
193 /* Look for DQS low, using quarter steps. */
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100194 printk(RAM_DEBUG, " Increasing medium until DQS LOW is found\n");
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100195 while (sampledqs(addr, lane, channel) != DQS_LOW) {
196 if (increase_medium(timing)) {
197 printk(BIOS_CRIT,
198 "Coarse > 15: DQS tuning failed, halt\n");
199 return -1;
200 }
201 program_timing(timing, channel, lane);
202 }
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100203 printk(RAM_DEBUG, " DQS low at coarse=%d medium=%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100204 timing->coarse, timing->medium);
205 return 0;
206}
207static int find_dqs_high(u8 channel, u8 lane, u32 addr,
208 struct rec_timing *timing)
209{
210 /* Look for DQS high, using quarter steps. */
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100211 printk(RAM_DEBUG, " Increasing medium until DQS HIGH is found\n");
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100212 while (sampledqs(addr, lane, channel) != DQS_HIGH) {
213 if (increase_medium(timing)) {
214 printk(BIOS_CRIT,
215 "Coarse > 16: DQS tuning failed, halt\n");
216 return -1;
217 }
218 program_timing(timing, channel, lane);
219 }
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100220 printk(RAM_DEBUG, " DQS high at coarse=%d medium=%d\n",
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100221 timing->coarse, timing->medium);
222 return 0;
223}
224
225static int find_dqs_edge_lowhigh(u8 channel, u8 lane,
226 u32 addr, struct rec_timing *timing)
227{
228 /* Medium search for DQS high. */
229 if (find_dqs_high(channel, lane, addr, timing))
230 return -1;
231
232 /* Go back and perform finer search. */
233 if (decrease_medium(timing))
234 return -1;
235 program_timing(timing, channel, lane);
236 if (fine_search_dqs_high(channel, lane, addr, timing) < 0)
237 return -1;
238
239 return 0;
240}
241
242static int find_preamble(u8 channel, u8 lane, u32 addr,
243 struct rec_timing *timing)
244{
245 /* Add a quarter step */
246 if (increase_medium(timing))
247 return -1;
248 program_timing(timing, channel, lane);
249 /* Verify we are at high */
250 if (sampledqs(addr, lane, channel) != DQS_HIGH) {
251 printk(BIOS_CRIT, "Not at DQS high, d'oh\n");
252 return -1;
253 }
254
255 /* Decrease coarse until LOW is found */
256 if (decr_coarse_low(channel, lane, addr, timing))
257 return -1;
258 return 0;
259}
260
261static int calibrate_receive_enable(u8 channel, u8 lane,
262 u32 addr, struct rec_timing *timing)
263{
264 program_timing(timing, channel, lane);
265 /* Set receive enable bit */
266 MCHBAR16(0x400 * channel + 0x588) = (MCHBAR16(0x400 * channel + 0x588)
267 & ~(3 << (lane * 2))) | (1 << (lane * 2));
268
269 if (find_dqs_low(channel, lane, addr, timing))
270 return -1;
271
272 /* Advance a little further. */
273 if (increase_medium(timing)) {
274 /* A finer search could be implemented */
275 printk(BIOS_WARNING, "Cannot increase medium further");
276 return -1;
277 }
278 program_timing(timing, channel, lane);
279
280 if (find_dqs_edge_lowhigh(channel, lane, addr, timing))
281 return -1;
282
283 /* Go back on fine search */
284 if (decrease_tap(timing))
285 return -1;
286 timing->pi = 3;
287 program_timing(timing, channel, lane);
288
289 if (find_preamble(channel, lane, addr, timing))
290 return -1;
291
292 if (find_dqs_edge_lowhigh(channel, lane, addr, timing))
293 return -1;
294 if (decrease_tap(timing))
295 return -1;
296 timing->pi = 7;
297 program_timing(timing, channel, lane);
298
299 /* Unset receive enable bit */
300 MCHBAR16(0x400 * channel + 0x588) = MCHBAR16(0x400 * channel + 0x588) &
301 ~(3 << (lane * 2));
302 return 0;
303}
304
Arthur Heymansadc571a2017-09-25 09:40:54 +0200305void rcven(struct sysinfo *s)
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100306{
Arthur Heymans1994e4482017-11-04 07:52:23 +0100307 int rank;
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100308 u8 channel, lane, reg8;
Arthur Heymans1994e4482017-11-04 07:52:23 +0100309 /*
310 * Using the macros below the compiler warns about this possibly being
311 * unitialised.
312 */
313 u32 addr = 0;
Arthur Heymans276049f2017-11-05 05:56:34 +0100314 struct rec_timing timing[TOTAL_BYTELANES];
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100315 u8 mincoarse;
316
Arthur Heymansa4e8f67b2017-12-16 21:04:46 +0100317 printk(BIOS_DEBUG, "Starting DQS receiver enable calibration\n");
318
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100319 MCHBAR8(0x5d8) = MCHBAR8(0x5d8) & ~0xc;
320 MCHBAR8(0x9d8) = MCHBAR8(0x9d8) & ~0xc;
321 MCHBAR8(0x5dc) = MCHBAR8(0x5dc) & ~0x80;
322 FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) {
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100323 mincoarse = 0xff;
Arthur Heymans1994e4482017-11-04 07:52:23 +0100324 /*
325 * Receive enable calibration happens on the first populated
326 * rank on each channel.
327 */
328 FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, channel, rank) {
329 addr = test_address(channel, rank);
330 break;
331 }
Arthur Heymans276049f2017-11-05 05:56:34 +0100332 FOR_EACH_BYTELANE(lane) {
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100333 printk(BIOS_DEBUG, "Channel %d, Lane %d addr=0x%08x\n",
334 channel, lane, addr);
335 timing[lane].coarse = (s->selected_timings.CAS + 1);
336 switch (lane) {
337 default:
338 case 0:
339 case 1:
340 timing[lane].medium = 0;
341 break;
342 case 2:
343 case 3:
344 timing[lane].medium = 1;
345 break;
346 case 4:
347 case 5:
348 timing[lane].medium = 2;
349 break;
350 case 6:
351 case 7:
352 timing[lane].medium = 3;
353 break;
354 }
355 timing[lane].tap = 0;
356 timing[lane].pi = 0;
357
358 if (calibrate_receive_enable(channel, lane, addr,
359 &timing[lane]))
360 die("Receive enable calibration failed\n");
361 if (mincoarse > timing[lane].coarse)
362 mincoarse = timing[lane].coarse;
363 }
364 printk(BIOS_DEBUG, "Found min coarse value = %d\n", mincoarse);
Arthur Heymansadc571a2017-09-25 09:40:54 +0200365 s->rcven_t[channel].min_common_coarse = mincoarse;
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100366 printk(BIOS_DEBUG, "Receive enable, final timings:\n");
367 /* Normalise coarse */
Arthur Heymans276049f2017-11-05 05:56:34 +0100368 FOR_EACH_BYTELANE(lane) {
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100369 if (timing[lane].coarse == 0)
370 reg8 = 0;
371 else
372 reg8 = timing[lane].coarse - mincoarse;
373 printk(BIOS_DEBUG, "ch %d lane %d: coarse offset: %d;"
374 "medium: %d; tap: %d\n",
375 channel, lane, reg8, timing[lane].medium,
376 timing[lane].tap);
Arthur Heymansadc571a2017-09-25 09:40:54 +0200377 s->rcven_t[channel].coarse_offset[lane] = reg8;
378 s->rcven_t[channel].medium[lane] = timing[lane].medium;
379 s->rcven_t[channel].tap[lane] = timing[lane].tap;
380 s->rcven_t[channel].pi[lane] = timing[lane].pi;
Arthur Heymansf6f4ba92017-12-11 07:36:15 +0100381 MCHBAR16(0x400 * channel + 0x5fa) =
382 (MCHBAR16(0x400 * channel + 0x5fa) &
383 ~(3 << (lane * 2))) | (reg8 << (lane * 2));
Arthur Heymans6d7a8c12017-03-07 20:48:14 +0100384 }
385 /* simply use timing[0] to program mincoarse */
386 timing[0].coarse = mincoarse;
387 program_timing(&timing[0], channel, 0);
388 }
389}