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