blob: 2271904ed51ab449fa3fde74101f4ed59135d6c9 [file] [log] [blame]
Angel Pons4b429832020-04-02 23:48:50 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
Stefan Reinauer278534d2008-10-29 04:51:07 +00003
Arthur Heymans186e9c42017-01-10 14:26:23 +01004#include <console/console.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +02005#include <device/mmio.h>
Stefan Reinauer278534d2008-10-29 04:51:07 +00006#include "raminit.h"
Arthur Heymans186e9c42017-01-10 14:26:23 +01007#include "i945.h"
Stefan Reinauer278534d2008-10-29 04:51:07 +00008
9/**
10 * sample the strobes signal
11 */
12static u32 sample_strobes(int channel_offset, struct sys_info *sysinfo)
13{
14 u32 reg32, addr;
15 int i;
16
17 MCHBAR32(C0DRC1 + channel_offset) |= (1 << 6);
Stefan Reinauer71a3d962009-07-21 21:44:24 +000018
Stefan Reinauer278534d2008-10-29 04:51:07 +000019 MCHBAR32(C0DRC1 + channel_offset) &= ~(1 << 6);
20
21 addr = 0;
22
23 if (channel_offset != 0) { /* must be dual channel */
Arthur Heymans70a8e342017-03-09 11:30:23 +010024 if (sysinfo->interleaved == 1)
Stefan Reinauer278534d2008-10-29 04:51:07 +000025 addr |= (1 << 6);
Arthur Heymans70a8e342017-03-09 11:30:23 +010026 else
Stefan Reinauer278534d2008-10-29 04:51:07 +000027 addr = ((u32)MCHBAR8(C0DRB3)) << 25;
Stefan Reinauer278534d2008-10-29 04:51:07 +000028 }
29
30 for (i = 0; i < 28; i++) {
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080031 read32((void *)addr);
32 read32((void *)(addr + 0x80));
Stefan Reinauer278534d2008-10-29 04:51:07 +000033 }
34
35 reg32 = MCHBAR32(RCVENMT);
Arthur Heymans70a8e342017-03-09 11:30:23 +010036 if (channel_offset == 0)
Stefan Reinauer278534d2008-10-29 04:51:07 +000037 reg32 = reg32 << 2;
Stefan Reinauer278534d2008-10-29 04:51:07 +000038
39 /**
40 * [19] = 1: all bits are high
41 * [18] = 1: all bits are low
42 * [19:18] = 00: bits are mixed high, low
43 */
44 return reg32;
45}
46
47/**
48 * This function sets receive enable coarse and medium timing parameters
49 */
50
51static void set_receive_enable(int channel_offset, u8 medium, u8 coarse)
52{
53 u32 reg32;
54
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010055 printk(BIOS_SPEW, " %s() medium=0x%x, coarse=0x%x\n", __func__, medium, coarse);
Stefan Reinauer71a3d962009-07-21 21:44:24 +000056
Stefan Reinauer278534d2008-10-29 04:51:07 +000057 reg32 = MCHBAR32(C0DRT1 + channel_offset);
58 reg32 &= 0xf0ffffff;
59 reg32 |= ((u32)coarse & 0x0f) << 24;
60 MCHBAR32(C0DRT1 + channel_offset) = reg32;
Stefan Reinauer71a3d962009-07-21 21:44:24 +000061
62 /* This should never happen: */
Stefan Reinauer278534d2008-10-29 04:51:07 +000063 if (coarse > 0x0f)
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010064 printk(BIOS_DEBUG, "%s: coarse overflow: 0x%02x.\n", __func__, coarse);
Stefan Reinauer278534d2008-10-29 04:51:07 +000065
66 /* medium control
67 *
68 * 00 - 1/4 clock
69 * 01 - 1/2 clock
70 * 10 - 3/4 clock
71 * 11 - 1 clock
72 */
73
74 reg32 = MCHBAR32(RCVENMT);
75 if (!channel_offset) {
Stefan Reinauer71a3d962009-07-21 21:44:24 +000076 /* Channel 0 */
Stefan Reinauer278534d2008-10-29 04:51:07 +000077 reg32 &= ~(3 << 2);
78 reg32 |= medium << 2;
79 } else {
Stefan Reinauer71a3d962009-07-21 21:44:24 +000080 /* Channel 1 */
Stefan Reinauer278534d2008-10-29 04:51:07 +000081 reg32 &= ~(3 << 0);
82 reg32 |= medium;
83 }
84 MCHBAR32(RCVENMT) = reg32;
85
86}
87
Arthur Heymans70a8e342017-03-09 11:30:23 +010088static int normalize(int channel_offset, u8 *mediumcoarse, u8 *fine)
Stefan Reinauer278534d2008-10-29 04:51:07 +000089{
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010090 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +000091
92 if (*fine < 0x80)
93 return 0;
94
95 *fine -= 0x80;
96 *mediumcoarse += 1;
97
98 if (*mediumcoarse >= 0x40) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000099 printk(BIOS_DEBUG, "Normalize Error\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000100 return -1;
101 }
102
103 set_receive_enable(channel_offset, *mediumcoarse & 3,
104 *mediumcoarse >> 2);
105
106 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
107
108 return 0;
109}
110
Arthur Heymans70a8e342017-03-09 11:30:23 +0100111static int find_preamble(int channel_offset, u8 *mediumcoarse,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000112 struct sys_info *sysinfo)
113{
114 /* find start of the data phase */
115 u32 reg32;
116
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100117 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000118
119 do {
120 if (*mediumcoarse < 4) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000121 printk(BIOS_DEBUG, "No Preamble found.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000122 return -1;
123 }
124 *mediumcoarse -= 4;
125
126 set_receive_enable(channel_offset, *mediumcoarse & 3,
127 *mediumcoarse >> 2);
128
129 reg32 = sample_strobes(channel_offset, sysinfo);
130
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000131 } while (reg32 & (1 << 19));
Stefan Reinauer278534d2008-10-29 04:51:07 +0000132
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000133 if (!(reg32 & (1 << 18))) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000134 printk(BIOS_DEBUG, "No Preamble found (neither high nor low).\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000135 return -1;
136 }
137
138 return 0;
139}
140
141/**
142 * add a quarter clock to the current receive enable settings
143 */
144
Arthur Heymans70a8e342017-03-09 11:30:23 +0100145static int add_quarter_clock(int channel_offset, u8 *mediumcoarse, u8 *fine)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000146{
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100147 printk(BIOS_SPEW, " %s() mediumcoarse=%02x fine=%02x\n", __func__,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000148 *mediumcoarse, *fine);
149 if (*fine >= 0x80) {
150 *fine -= 0x80;
151
152 *mediumcoarse += 2;
153 if (*mediumcoarse >= 0x40) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000154 printk(BIOS_DEBUG, "clocks at max.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000155 return -1;
156 }
157
158 set_receive_enable(channel_offset, *mediumcoarse & 3,
159 *mediumcoarse >> 2);
160 } else {
161 *fine += 0x80;
162 }
163
164 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
165
166 return 0;
167}
168
Arthur Heymans70a8e342017-03-09 11:30:23 +0100169static int find_strobes_low(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000170 struct sys_info *sysinfo)
171{
172 u32 rcvenmt;
173
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100174 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000175
Stefan Reinauer278534d2008-10-29 04:51:07 +0000176 for (;;) {
177 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
178
179 set_receive_enable(channel_offset, *mediumcoarse & 3,
180 *mediumcoarse >> 2);
181
182 rcvenmt = sample_strobes(channel_offset, sysinfo);
183
184 if (((rcvenmt & (1 << 18)) != 0))
185 return 0;
186
Stefan Reinauer278534d2008-10-29 04:51:07 +0000187 *fine -= 0x80;
188 if (*fine == 0)
189 continue;
190
191 *mediumcoarse -= 2;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000192 if (*mediumcoarse < 0xfe)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000193 continue;
194
195 break;
196
197 }
198
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000199 printk(BIOS_DEBUG, "Could not find low strobe\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000200 return 0;
201}
202
Arthur Heymans70a8e342017-03-09 11:30:23 +0100203static int find_strobes_edge(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000204 struct sys_info *sysinfo)
205{
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000206
Stefan Reinauer278534d2008-10-29 04:51:07 +0000207 int counter;
208 u32 rcvenmt;
209
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100210 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000211
212 counter = 8;
213 set_receive_enable(channel_offset, *mediumcoarse & 3,
214 *mediumcoarse >> 2);
215
216 for (;;) {
217 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
218 rcvenmt = sample_strobes(channel_offset, sysinfo);
219
220 if ((rcvenmt & (1 << 19)) == 0) {
221 counter = 8;
222 } else {
223 counter--;
224 if (!counter)
225 break;
226 }
227
228 *fine = *fine + 1;
229 if (*fine < 0xf8) {
230 if (*fine & (1 << 3)) {
231 *fine &= ~(1 << 3);
232 *fine += 0x10;
233 }
234 continue;
235 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000236
Stefan Reinauer278534d2008-10-29 04:51:07 +0000237 *fine = 0;
238 *mediumcoarse += 2;
239 if (*mediumcoarse <= 0x40) {
240 set_receive_enable(channel_offset, *mediumcoarse & 3,
241 *mediumcoarse >> 2);
242 continue;
243 }
244
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000245 printk(BIOS_DEBUG, "Could not find rising edge.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000246 return -1;
247 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000248
Stefan Reinauer278534d2008-10-29 04:51:07 +0000249 *fine -= 7;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000250 if (*fine >= 0xf9) {
Stefan Reinauer278534d2008-10-29 04:51:07 +0000251 *mediumcoarse -= 2;
252 set_receive_enable(channel_offset, *mediumcoarse & 3,
253 *mediumcoarse >> 2);
254 }
255
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000256 *fine &= ~(1 << 3);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000257 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
258
259 return 0;
260}
261
262/**
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000263 * Here we use a trick. The RCVEN channel 0 registers are all at an
Stefan Reinauer278534d2008-10-29 04:51:07 +0000264 * offset of 0x80 to the channel 0 registers. We don't want to waste
Elyes HAOUAS12df9502016-08-23 21:29:48 +0200265 * a lot of if ()s so let's just pass 0 or 0x80 for the channel offset.
Stefan Reinauer278534d2008-10-29 04:51:07 +0000266 */
267
268static int receive_enable_autoconfig(int channel_offset,
269 struct sys_info *sysinfo)
270{
271 u8 mediumcoarse;
272 u8 fine;
273
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100274 printk(BIOS_SPEW, "%s() for channel %d\n", __func__, channel_offset ? 1 : 0);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000275
276 /* Set initial values */
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000277 mediumcoarse = (sysinfo->cas << 2) | 3;
Stefan Reinauer278534d2008-10-29 04:51:07 +0000278 fine = 0;
279
280 if (find_strobes_low(channel_offset, &mediumcoarse, &fine, sysinfo))
281 return -1;
282
283 if (find_strobes_edge(channel_offset, &mediumcoarse, &fine, sysinfo))
284 return -1;
285
286 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
287 return -1;
288
289 if (find_preamble(channel_offset, &mediumcoarse, sysinfo))
290 return -1;
291
292 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
293 return -1;
294
295 if (normalize(channel_offset, &mediumcoarse, &fine))
296 return -1;
297
298 /* This is a debug check to see if the rcven code is fully working.
299 * It can be removed when the output message is not printed anymore
300 */
Arthur Heymans70a8e342017-03-09 11:30:23 +0100301 if (MCHBAR8(C0WL0REOST + channel_offset) == 0)
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000302 printk(BIOS_DEBUG, "Weird. No C%sWL0REOST\n", channel_offset?"1":"0");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000303
304 return 0;
305}
306
307void receive_enable_adjust(struct sys_info *sysinfo)
308{
309 /* Is channel 0 populated? */
310 if (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED
311 || sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED)
312 if (receive_enable_autoconfig(0, sysinfo))
313 return;
314
315 /* Is channel 1 populated? */
316 if (sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED
317 || sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED)
318 if (receive_enable_autoconfig(0x80, sysinfo))
319 return;
320}