blob: b95170f7c578609c567a6cd4c30b3697ae79f414 [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
Angel Pons1d4044a2021-03-27 19:11:51 +010016 mchbar_setbits32(C0DRC1 + channel_offset, 1 << 6);
17 mchbar_clrbits32(C0DRC1 + channel_offset, 1 << 6);
Stefan Reinauer278534d2008-10-29 04:51:07 +000018
19 addr = 0;
20
21 if (channel_offset != 0) { /* must be dual channel */
Arthur Heymans70a8e342017-03-09 11:30:23 +010022 if (sysinfo->interleaved == 1)
Stefan Reinauer278534d2008-10-29 04:51:07 +000023 addr |= (1 << 6);
Arthur Heymans70a8e342017-03-09 11:30:23 +010024 else
Angel Pons1d4044a2021-03-27 19:11:51 +010025 addr = ((u32)mchbar_read8(C0DRB3)) << 25;
Stefan Reinauer278534d2008-10-29 04:51:07 +000026 }
27
28 for (i = 0; i < 28; i++) {
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080029 read32((void *)addr);
30 read32((void *)(addr + 0x80));
Stefan Reinauer278534d2008-10-29 04:51:07 +000031 }
32
Angel Pons1d4044a2021-03-27 19:11:51 +010033 reg32 = mchbar_read32(RCVENMT);
Arthur Heymans70a8e342017-03-09 11:30:23 +010034 if (channel_offset == 0)
Stefan Reinauer278534d2008-10-29 04:51:07 +000035 reg32 = reg32 << 2;
Stefan Reinauer278534d2008-10-29 04:51:07 +000036
37 /**
38 * [19] = 1: all bits are high
39 * [18] = 1: all bits are low
40 * [19:18] = 00: bits are mixed high, low
41 */
42 return reg32;
43}
44
45/**
46 * This function sets receive enable coarse and medium timing parameters
47 */
48
49static void set_receive_enable(int channel_offset, u8 medium, u8 coarse)
50{
51 u32 reg32;
52
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010053 printk(BIOS_SPEW, " %s() medium=0x%x, coarse=0x%x\n", __func__, medium, coarse);
Stefan Reinauer71a3d962009-07-21 21:44:24 +000054
Angel Pons1d4044a2021-03-27 19:11:51 +010055 reg32 = mchbar_read32(C0DRT1 + channel_offset);
Stefan Reinauer278534d2008-10-29 04:51:07 +000056 reg32 &= 0xf0ffffff;
57 reg32 |= ((u32)coarse & 0x0f) << 24;
Angel Pons1d4044a2021-03-27 19:11:51 +010058 mchbar_write32(C0DRT1 + channel_offset, reg32);
Stefan Reinauer71a3d962009-07-21 21:44:24 +000059
60 /* This should never happen: */
Stefan Reinauer278534d2008-10-29 04:51:07 +000061 if (coarse > 0x0f)
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010062 printk(BIOS_DEBUG, "%s: coarse overflow: 0x%02x.\n", __func__, coarse);
Stefan Reinauer278534d2008-10-29 04:51:07 +000063
64 /* medium control
65 *
66 * 00 - 1/4 clock
67 * 01 - 1/2 clock
68 * 10 - 3/4 clock
69 * 11 - 1 clock
70 */
71
Angel Pons1d4044a2021-03-27 19:11:51 +010072 reg32 = mchbar_read32(RCVENMT);
Stefan Reinauer278534d2008-10-29 04:51:07 +000073 if (!channel_offset) {
Stefan Reinauer71a3d962009-07-21 21:44:24 +000074 /* Channel 0 */
Stefan Reinauer278534d2008-10-29 04:51:07 +000075 reg32 &= ~(3 << 2);
76 reg32 |= medium << 2;
77 } else {
Stefan Reinauer71a3d962009-07-21 21:44:24 +000078 /* Channel 1 */
Stefan Reinauer278534d2008-10-29 04:51:07 +000079 reg32 &= ~(3 << 0);
80 reg32 |= medium;
81 }
Angel Pons1d4044a2021-03-27 19:11:51 +010082 mchbar_write32(RCVENMT, reg32);
Stefan Reinauer278534d2008-10-29 04:51:07 +000083
84}
85
Arthur Heymans70a8e342017-03-09 11:30:23 +010086static int normalize(int channel_offset, u8 *mediumcoarse, u8 *fine)
Stefan Reinauer278534d2008-10-29 04:51:07 +000087{
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010088 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +000089
90 if (*fine < 0x80)
91 return 0;
92
93 *fine -= 0x80;
94 *mediumcoarse += 1;
95
96 if (*mediumcoarse >= 0x40) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000097 printk(BIOS_DEBUG, "Normalize Error\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +000098 return -1;
99 }
100
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200101 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000102
Angel Pons1d4044a2021-03-27 19:11:51 +0100103 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000104
105 return 0;
106}
107
Arthur Heymans70a8e342017-03-09 11:30:23 +0100108static int find_preamble(int channel_offset, u8 *mediumcoarse,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000109 struct sys_info *sysinfo)
110{
111 /* find start of the data phase */
112 u32 reg32;
113
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100114 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000115
116 do {
117 if (*mediumcoarse < 4) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000118 printk(BIOS_DEBUG, "No Preamble found.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000119 return -1;
120 }
121 *mediumcoarse -= 4;
122
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200123 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000124
125 reg32 = sample_strobes(channel_offset, sysinfo);
126
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000127 } while (reg32 & (1 << 19));
Stefan Reinauer278534d2008-10-29 04:51:07 +0000128
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000129 if (!(reg32 & (1 << 18))) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000130 printk(BIOS_DEBUG, "No Preamble found (neither high nor low).\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000131 return -1;
132 }
133
134 return 0;
135}
136
137/**
138 * add a quarter clock to the current receive enable settings
139 */
140
Arthur Heymans70a8e342017-03-09 11:30:23 +0100141static int add_quarter_clock(int channel_offset, u8 *mediumcoarse, u8 *fine)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000142{
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100143 printk(BIOS_SPEW, " %s() mediumcoarse=%02x fine=%02x\n", __func__,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000144 *mediumcoarse, *fine);
145 if (*fine >= 0x80) {
146 *fine -= 0x80;
147
148 *mediumcoarse += 2;
149 if (*mediumcoarse >= 0x40) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000150 printk(BIOS_DEBUG, "clocks at max.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000151 return -1;
152 }
153
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200154 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000155 } else {
156 *fine += 0x80;
157 }
158
Angel Pons1d4044a2021-03-27 19:11:51 +0100159 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000160
161 return 0;
162}
163
Arthur Heymans70a8e342017-03-09 11:30:23 +0100164static int find_strobes_low(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000165 struct sys_info *sysinfo)
166{
167 u32 rcvenmt;
168
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100169 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000170
Stefan Reinauer278534d2008-10-29 04:51:07 +0000171 for (;;) {
Angel Pons1d4044a2021-03-27 19:11:51 +0100172 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000173
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200174 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000175
176 rcvenmt = sample_strobes(channel_offset, sysinfo);
177
178 if (((rcvenmt & (1 << 18)) != 0))
179 return 0;
180
Stefan Reinauer278534d2008-10-29 04:51:07 +0000181 *fine -= 0x80;
182 if (*fine == 0)
183 continue;
184
185 *mediumcoarse -= 2;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000186 if (*mediumcoarse < 0xfe)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000187 continue;
188
189 break;
190
191 }
192
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000193 printk(BIOS_DEBUG, "Could not find low strobe\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000194 return 0;
195}
196
Arthur Heymans70a8e342017-03-09 11:30:23 +0100197static int find_strobes_edge(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000198 struct sys_info *sysinfo)
199{
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000200
Stefan Reinauer278534d2008-10-29 04:51:07 +0000201 int counter;
202 u32 rcvenmt;
203
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100204 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000205
206 counter = 8;
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200207 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000208
209 for (;;) {
Angel Pons1d4044a2021-03-27 19:11:51 +0100210 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000211 rcvenmt = sample_strobes(channel_offset, sysinfo);
212
213 if ((rcvenmt & (1 << 19)) == 0) {
214 counter = 8;
215 } else {
216 counter--;
217 if (!counter)
218 break;
219 }
220
221 *fine = *fine + 1;
222 if (*fine < 0xf8) {
223 if (*fine & (1 << 3)) {
224 *fine &= ~(1 << 3);
225 *fine += 0x10;
226 }
227 continue;
228 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000229
Stefan Reinauer278534d2008-10-29 04:51:07 +0000230 *fine = 0;
231 *mediumcoarse += 2;
232 if (*mediumcoarse <= 0x40) {
233 set_receive_enable(channel_offset, *mediumcoarse & 3,
234 *mediumcoarse >> 2);
235 continue;
236 }
237
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000238 printk(BIOS_DEBUG, "Could not find rising edge.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000239 return -1;
240 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000241
Stefan Reinauer278534d2008-10-29 04:51:07 +0000242 *fine -= 7;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000243 if (*fine >= 0xf9) {
Stefan Reinauer278534d2008-10-29 04:51:07 +0000244 *mediumcoarse -= 2;
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200245 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000246 }
247
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000248 *fine &= ~(1 << 3);
Angel Pons1d4044a2021-03-27 19:11:51 +0100249 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000250
251 return 0;
252}
253
254/**
Angel Pons22aeed32020-06-11 13:28:03 +0200255 * Here we use a trick. The RCVEN channel 1 registers are all at an
Stefan Reinauer278534d2008-10-29 04:51:07 +0000256 * offset of 0x80 to the channel 0 registers. We don't want to waste
Elyes HAOUAS12df9502016-08-23 21:29:48 +0200257 * a lot of if ()s so let's just pass 0 or 0x80 for the channel offset.
Stefan Reinauer278534d2008-10-29 04:51:07 +0000258 */
259
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200260static int receive_enable_autoconfig(int channel_offset, struct sys_info *sysinfo)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000261{
262 u8 mediumcoarse;
263 u8 fine;
264
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100265 printk(BIOS_SPEW, "%s() for channel %d\n", __func__, channel_offset ? 1 : 0);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000266
267 /* Set initial values */
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000268 mediumcoarse = (sysinfo->cas << 2) | 3;
Stefan Reinauer278534d2008-10-29 04:51:07 +0000269 fine = 0;
270
271 if (find_strobes_low(channel_offset, &mediumcoarse, &fine, sysinfo))
272 return -1;
273
274 if (find_strobes_edge(channel_offset, &mediumcoarse, &fine, sysinfo))
275 return -1;
276
277 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
278 return -1;
279
280 if (find_preamble(channel_offset, &mediumcoarse, sysinfo))
281 return -1;
282
283 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
284 return -1;
285
286 if (normalize(channel_offset, &mediumcoarse, &fine))
287 return -1;
288
289 /* This is a debug check to see if the rcven code is fully working.
290 * It can be removed when the output message is not printed anymore
291 */
Angel Pons1d4044a2021-03-27 19:11:51 +0100292 if (mchbar_read8(C0WL0REOST + channel_offset) == 0)
Angel Pons30492572020-06-11 13:24:54 +0200293 printk(BIOS_DEBUG, "Weird. No C%sWL0REOST\n", channel_offset ? "1" : "0");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000294
295 return 0;
296}
297
298void receive_enable_adjust(struct sys_info *sysinfo)
299{
300 /* Is channel 0 populated? */
301 if (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED
302 || sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED)
303 if (receive_enable_autoconfig(0, sysinfo))
304 return;
305
306 /* Is channel 1 populated? */
307 if (sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED
308 || sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED)
309 if (receive_enable_autoconfig(0x80, sysinfo))
310 return;
311}