blob: 84814f73555488ac79f0cfa5ede23c76dfba2c5f [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
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200103 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000104
105 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
106
107 return 0;
108}
109
Arthur Heymans70a8e342017-03-09 11:30:23 +0100110static int find_preamble(int channel_offset, u8 *mediumcoarse,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000111 struct sys_info *sysinfo)
112{
113 /* find start of the data phase */
114 u32 reg32;
115
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100116 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000117
118 do {
119 if (*mediumcoarse < 4) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000120 printk(BIOS_DEBUG, "No Preamble found.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000121 return -1;
122 }
123 *mediumcoarse -= 4;
124
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200125 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000126
127 reg32 = sample_strobes(channel_offset, sysinfo);
128
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000129 } while (reg32 & (1 << 19));
Stefan Reinauer278534d2008-10-29 04:51:07 +0000130
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000131 if (!(reg32 & (1 << 18))) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000132 printk(BIOS_DEBUG, "No Preamble found (neither high nor low).\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000133 return -1;
134 }
135
136 return 0;
137}
138
139/**
140 * add a quarter clock to the current receive enable settings
141 */
142
Arthur Heymans70a8e342017-03-09 11:30:23 +0100143static int add_quarter_clock(int channel_offset, u8 *mediumcoarse, u8 *fine)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000144{
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100145 printk(BIOS_SPEW, " %s() mediumcoarse=%02x fine=%02x\n", __func__,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000146 *mediumcoarse, *fine);
147 if (*fine >= 0x80) {
148 *fine -= 0x80;
149
150 *mediumcoarse += 2;
151 if (*mediumcoarse >= 0x40) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000152 printk(BIOS_DEBUG, "clocks at max.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000153 return -1;
154 }
155
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200156 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000157 } else {
158 *fine += 0x80;
159 }
160
161 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
162
163 return 0;
164}
165
Arthur Heymans70a8e342017-03-09 11:30:23 +0100166static int find_strobes_low(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000167 struct sys_info *sysinfo)
168{
169 u32 rcvenmt;
170
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100171 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000172
Stefan Reinauer278534d2008-10-29 04:51:07 +0000173 for (;;) {
174 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
175
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200176 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000177
178 rcvenmt = sample_strobes(channel_offset, sysinfo);
179
180 if (((rcvenmt & (1 << 18)) != 0))
181 return 0;
182
Stefan Reinauer278534d2008-10-29 04:51:07 +0000183 *fine -= 0x80;
184 if (*fine == 0)
185 continue;
186
187 *mediumcoarse -= 2;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000188 if (*mediumcoarse < 0xfe)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000189 continue;
190
191 break;
192
193 }
194
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000195 printk(BIOS_DEBUG, "Could not find low strobe\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000196 return 0;
197}
198
Arthur Heymans70a8e342017-03-09 11:30:23 +0100199static int find_strobes_edge(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000200 struct sys_info *sysinfo)
201{
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000202
Stefan Reinauer278534d2008-10-29 04:51:07 +0000203 int counter;
204 u32 rcvenmt;
205
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100206 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000207
208 counter = 8;
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200209 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000210
211 for (;;) {
212 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
213 rcvenmt = sample_strobes(channel_offset, sysinfo);
214
215 if ((rcvenmt & (1 << 19)) == 0) {
216 counter = 8;
217 } else {
218 counter--;
219 if (!counter)
220 break;
221 }
222
223 *fine = *fine + 1;
224 if (*fine < 0xf8) {
225 if (*fine & (1 << 3)) {
226 *fine &= ~(1 << 3);
227 *fine += 0x10;
228 }
229 continue;
230 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000231
Stefan Reinauer278534d2008-10-29 04:51:07 +0000232 *fine = 0;
233 *mediumcoarse += 2;
234 if (*mediumcoarse <= 0x40) {
235 set_receive_enable(channel_offset, *mediumcoarse & 3,
236 *mediumcoarse >> 2);
237 continue;
238 }
239
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000240 printk(BIOS_DEBUG, "Could not find rising edge.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000241 return -1;
242 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000243
Stefan Reinauer278534d2008-10-29 04:51:07 +0000244 *fine -= 7;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000245 if (*fine >= 0xf9) {
Stefan Reinauer278534d2008-10-29 04:51:07 +0000246 *mediumcoarse -= 2;
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200247 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000248 }
249
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000250 *fine &= ~(1 << 3);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000251 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
252
253 return 0;
254}
255
256/**
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000257 * Here we use a trick. The RCVEN channel 0 registers are all at an
Stefan Reinauer278534d2008-10-29 04:51:07 +0000258 * offset of 0x80 to the channel 0 registers. We don't want to waste
Elyes HAOUAS12df9502016-08-23 21:29:48 +0200259 * a lot of if ()s so let's just pass 0 or 0x80 for the channel offset.
Stefan Reinauer278534d2008-10-29 04:51:07 +0000260 */
261
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200262static int receive_enable_autoconfig(int channel_offset, struct sys_info *sysinfo)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000263{
264 u8 mediumcoarse;
265 u8 fine;
266
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100267 printk(BIOS_SPEW, "%s() for channel %d\n", __func__, channel_offset ? 1 : 0);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000268
269 /* Set initial values */
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000270 mediumcoarse = (sysinfo->cas << 2) | 3;
Stefan Reinauer278534d2008-10-29 04:51:07 +0000271 fine = 0;
272
273 if (find_strobes_low(channel_offset, &mediumcoarse, &fine, sysinfo))
274 return -1;
275
276 if (find_strobes_edge(channel_offset, &mediumcoarse, &fine, sysinfo))
277 return -1;
278
279 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
280 return -1;
281
282 if (find_preamble(channel_offset, &mediumcoarse, sysinfo))
283 return -1;
284
285 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
286 return -1;
287
288 if (normalize(channel_offset, &mediumcoarse, &fine))
289 return -1;
290
291 /* This is a debug check to see if the rcven code is fully working.
292 * It can be removed when the output message is not printed anymore
293 */
Arthur Heymans70a8e342017-03-09 11:30:23 +0100294 if (MCHBAR8(C0WL0REOST + channel_offset) == 0)
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000295 printk(BIOS_DEBUG, "Weird. No C%sWL0REOST\n", channel_offset?"1":"0");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000296
297 return 0;
298}
299
300void receive_enable_adjust(struct sys_info *sysinfo)
301{
302 /* Is channel 0 populated? */
303 if (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED
304 || sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED)
305 if (receive_enable_autoconfig(0, sysinfo))
306 return;
307
308 /* Is channel 1 populated? */
309 if (sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED
310 || sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED)
311 if (receive_enable_autoconfig(0x80, sysinfo))
312 return;
313}