blob: ea37d9c4f3c24aeafc56e42dcf6751cbd555b1bf [file] [log] [blame]
Angel Pons4b429832020-04-02 23:48:50 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Stefan Reinauer278534d2008-10-29 04:51:07 +00002
Arthur Heymans186e9c42017-01-10 14:26:23 +01003#include <console/console.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +02004#include <device/mmio.h>
Stefan Reinauer278534d2008-10-29 04:51:07 +00005#include "raminit.h"
Arthur Heymans186e9c42017-01-10 14:26:23 +01006#include "i945.h"
Stefan Reinauer278534d2008-10-29 04:51:07 +00007
8/**
9 * sample the strobes signal
10 */
11static u32 sample_strobes(int channel_offset, struct sys_info *sysinfo)
12{
13 u32 reg32, addr;
14 int i;
15
16 MCHBAR32(C0DRC1 + channel_offset) |= (1 << 6);
Stefan Reinauer71a3d962009-07-21 21:44:24 +000017
Stefan Reinauer278534d2008-10-29 04:51:07 +000018 MCHBAR32(C0DRC1 + channel_offset) &= ~(1 << 6);
19
20 addr = 0;
21
22 if (channel_offset != 0) { /* must be dual channel */
Arthur Heymans70a8e342017-03-09 11:30:23 +010023 if (sysinfo->interleaved == 1)
Stefan Reinauer278534d2008-10-29 04:51:07 +000024 addr |= (1 << 6);
Arthur Heymans70a8e342017-03-09 11:30:23 +010025 else
Stefan Reinauer278534d2008-10-29 04:51:07 +000026 addr = ((u32)MCHBAR8(C0DRB3)) << 25;
Stefan Reinauer278534d2008-10-29 04:51:07 +000027 }
28
29 for (i = 0; i < 28; i++) {
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080030 read32((void *)addr);
31 read32((void *)(addr + 0x80));
Stefan Reinauer278534d2008-10-29 04:51:07 +000032 }
33
34 reg32 = MCHBAR32(RCVENMT);
Arthur Heymans70a8e342017-03-09 11:30:23 +010035 if (channel_offset == 0)
Stefan Reinauer278534d2008-10-29 04:51:07 +000036 reg32 = reg32 << 2;
Stefan Reinauer278534d2008-10-29 04:51:07 +000037
38 /**
39 * [19] = 1: all bits are high
40 * [18] = 1: all bits are low
41 * [19:18] = 00: bits are mixed high, low
42 */
43 return reg32;
44}
45
46/**
47 * This function sets receive enable coarse and medium timing parameters
48 */
49
50static void set_receive_enable(int channel_offset, u8 medium, u8 coarse)
51{
52 u32 reg32;
53
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010054 printk(BIOS_SPEW, " %s() medium=0x%x, coarse=0x%x\n", __func__, medium, coarse);
Stefan Reinauer71a3d962009-07-21 21:44:24 +000055
Stefan Reinauer278534d2008-10-29 04:51:07 +000056 reg32 = MCHBAR32(C0DRT1 + channel_offset);
57 reg32 &= 0xf0ffffff;
58 reg32 |= ((u32)coarse & 0x0f) << 24;
59 MCHBAR32(C0DRT1 + channel_offset) = reg32;
Stefan Reinauer71a3d962009-07-21 21:44:24 +000060
61 /* This should never happen: */
Stefan Reinauer278534d2008-10-29 04:51:07 +000062 if (coarse > 0x0f)
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010063 printk(BIOS_DEBUG, "%s: coarse overflow: 0x%02x.\n", __func__, coarse);
Stefan Reinauer278534d2008-10-29 04:51:07 +000064
65 /* medium control
66 *
67 * 00 - 1/4 clock
68 * 01 - 1/2 clock
69 * 10 - 3/4 clock
70 * 11 - 1 clock
71 */
72
73 reg32 = MCHBAR32(RCVENMT);
74 if (!channel_offset) {
Stefan Reinauer71a3d962009-07-21 21:44:24 +000075 /* Channel 0 */
Stefan Reinauer278534d2008-10-29 04:51:07 +000076 reg32 &= ~(3 << 2);
77 reg32 |= medium << 2;
78 } else {
Stefan Reinauer71a3d962009-07-21 21:44:24 +000079 /* Channel 1 */
Stefan Reinauer278534d2008-10-29 04:51:07 +000080 reg32 &= ~(3 << 0);
81 reg32 |= medium;
82 }
83 MCHBAR32(RCVENMT) = reg32;
84
85}
86
Arthur Heymans70a8e342017-03-09 11:30:23 +010087static int normalize(int channel_offset, u8 *mediumcoarse, u8 *fine)
Stefan Reinauer278534d2008-10-29 04:51:07 +000088{
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010089 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +000090
91 if (*fine < 0x80)
92 return 0;
93
94 *fine -= 0x80;
95 *mediumcoarse += 1;
96
97 if (*mediumcoarse >= 0x40) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000098 printk(BIOS_DEBUG, "Normalize Error\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +000099 return -1;
100 }
101
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200102 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000103
104 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
105
106 return 0;
107}
108
Arthur Heymans70a8e342017-03-09 11:30:23 +0100109static int find_preamble(int channel_offset, u8 *mediumcoarse,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000110 struct sys_info *sysinfo)
111{
112 /* find start of the data phase */
113 u32 reg32;
114
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100115 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000116
117 do {
118 if (*mediumcoarse < 4) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000119 printk(BIOS_DEBUG, "No Preamble found.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000120 return -1;
121 }
122 *mediumcoarse -= 4;
123
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200124 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000125
126 reg32 = sample_strobes(channel_offset, sysinfo);
127
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000128 } while (reg32 & (1 << 19));
Stefan Reinauer278534d2008-10-29 04:51:07 +0000129
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000130 if (!(reg32 & (1 << 18))) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000131 printk(BIOS_DEBUG, "No Preamble found (neither high nor low).\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000132 return -1;
133 }
134
135 return 0;
136}
137
138/**
139 * add a quarter clock to the current receive enable settings
140 */
141
Arthur Heymans70a8e342017-03-09 11:30:23 +0100142static int add_quarter_clock(int channel_offset, u8 *mediumcoarse, u8 *fine)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000143{
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100144 printk(BIOS_SPEW, " %s() mediumcoarse=%02x fine=%02x\n", __func__,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000145 *mediumcoarse, *fine);
146 if (*fine >= 0x80) {
147 *fine -= 0x80;
148
149 *mediumcoarse += 2;
150 if (*mediumcoarse >= 0x40) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000151 printk(BIOS_DEBUG, "clocks at max.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000152 return -1;
153 }
154
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200155 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000156 } else {
157 *fine += 0x80;
158 }
159
160 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
161
162 return 0;
163}
164
Arthur Heymans70a8e342017-03-09 11:30:23 +0100165static int find_strobes_low(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000166 struct sys_info *sysinfo)
167{
168 u32 rcvenmt;
169
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100170 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000171
Stefan Reinauer278534d2008-10-29 04:51:07 +0000172 for (;;) {
173 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
174
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200175 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000176
177 rcvenmt = sample_strobes(channel_offset, sysinfo);
178
179 if (((rcvenmt & (1 << 18)) != 0))
180 return 0;
181
Stefan Reinauer278534d2008-10-29 04:51:07 +0000182 *fine -= 0x80;
183 if (*fine == 0)
184 continue;
185
186 *mediumcoarse -= 2;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000187 if (*mediumcoarse < 0xfe)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000188 continue;
189
190 break;
191
192 }
193
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000194 printk(BIOS_DEBUG, "Could not find low strobe\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000195 return 0;
196}
197
Arthur Heymans70a8e342017-03-09 11:30:23 +0100198static int find_strobes_edge(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000199 struct sys_info *sysinfo)
200{
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000201
Stefan Reinauer278534d2008-10-29 04:51:07 +0000202 int counter;
203 u32 rcvenmt;
204
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100205 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000206
207 counter = 8;
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200208 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000209
210 for (;;) {
211 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
212 rcvenmt = sample_strobes(channel_offset, sysinfo);
213
214 if ((rcvenmt & (1 << 19)) == 0) {
215 counter = 8;
216 } else {
217 counter--;
218 if (!counter)
219 break;
220 }
221
222 *fine = *fine + 1;
223 if (*fine < 0xf8) {
224 if (*fine & (1 << 3)) {
225 *fine &= ~(1 << 3);
226 *fine += 0x10;
227 }
228 continue;
229 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000230
Stefan Reinauer278534d2008-10-29 04:51:07 +0000231 *fine = 0;
232 *mediumcoarse += 2;
233 if (*mediumcoarse <= 0x40) {
234 set_receive_enable(channel_offset, *mediumcoarse & 3,
235 *mediumcoarse >> 2);
236 continue;
237 }
238
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000239 printk(BIOS_DEBUG, "Could not find rising edge.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000240 return -1;
241 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000242
Stefan Reinauer278534d2008-10-29 04:51:07 +0000243 *fine -= 7;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000244 if (*fine >= 0xf9) {
Stefan Reinauer278534d2008-10-29 04:51:07 +0000245 *mediumcoarse -= 2;
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200246 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000247 }
248
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000249 *fine &= ~(1 << 3);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000250 MCHBAR8(C0WL0REOST + channel_offset) = *fine;
251
252 return 0;
253}
254
255/**
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000256 * Here we use a trick. The RCVEN channel 0 registers are all at an
Stefan Reinauer278534d2008-10-29 04:51:07 +0000257 * offset of 0x80 to the channel 0 registers. We don't want to waste
Elyes HAOUAS12df9502016-08-23 21:29:48 +0200258 * a lot of if ()s so let's just pass 0 or 0x80 for the channel offset.
Stefan Reinauer278534d2008-10-29 04:51:07 +0000259 */
260
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200261static int receive_enable_autoconfig(int channel_offset, struct sys_info *sysinfo)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000262{
263 u8 mediumcoarse;
264 u8 fine;
265
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100266 printk(BIOS_SPEW, "%s() for channel %d\n", __func__, channel_offset ? 1 : 0);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000267
268 /* Set initial values */
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000269 mediumcoarse = (sysinfo->cas << 2) | 3;
Stefan Reinauer278534d2008-10-29 04:51:07 +0000270 fine = 0;
271
272 if (find_strobes_low(channel_offset, &mediumcoarse, &fine, sysinfo))
273 return -1;
274
275 if (find_strobes_edge(channel_offset, &mediumcoarse, &fine, sysinfo))
276 return -1;
277
278 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
279 return -1;
280
281 if (find_preamble(channel_offset, &mediumcoarse, sysinfo))
282 return -1;
283
284 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
285 return -1;
286
287 if (normalize(channel_offset, &mediumcoarse, &fine))
288 return -1;
289
290 /* This is a debug check to see if the rcven code is fully working.
291 * It can be removed when the output message is not printed anymore
292 */
Arthur Heymans70a8e342017-03-09 11:30:23 +0100293 if (MCHBAR8(C0WL0REOST + channel_offset) == 0)
Angel Pons30492572020-06-11 13:24:54 +0200294 printk(BIOS_DEBUG, "Weird. No C%sWL0REOST\n", channel_offset ? "1" : "0");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000295
296 return 0;
297}
298
299void receive_enable_adjust(struct sys_info *sysinfo)
300{
301 /* Is channel 0 populated? */
302 if (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED
303 || sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED)
304 if (receive_enable_autoconfig(0, sysinfo))
305 return;
306
307 /* Is channel 1 populated? */
308 if (sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED
309 || sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED)
310 if (receive_enable_autoconfig(0x80, sysinfo))
311 return;
312}