blob: 37b568a35171dfc93e43e448f6ac6511719d1121 [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>
Elyes Haouas7132b622022-10-02 13:50:23 +02005#include <types.h>
6
Arthur Heymans186e9c42017-01-10 14:26:23 +01007#include "i945.h"
Elyes Haouas7132b622022-10-02 13:50:23 +02008#include "raminit.h"
Stefan Reinauer278534d2008-10-29 04:51:07 +00009
10/**
11 * sample the strobes signal
12 */
13static u32 sample_strobes(int channel_offset, struct sys_info *sysinfo)
14{
Elyes HAOUAS964055d2022-01-14 18:56:49 +010015 u32 reg32;
16 uintptr_t addr;
Stefan Reinauer278534d2008-10-29 04:51:07 +000017 int i;
18
Angel Pons1d4044a2021-03-27 19:11:51 +010019 mchbar_setbits32(C0DRC1 + channel_offset, 1 << 6);
20 mchbar_clrbits32(C0DRC1 + channel_offset, 1 << 6);
Stefan Reinauer278534d2008-10-29 04:51:07 +000021
22 addr = 0;
23
24 if (channel_offset != 0) { /* must be dual channel */
Arthur Heymans70a8e342017-03-09 11:30:23 +010025 if (sysinfo->interleaved == 1)
Stefan Reinauer278534d2008-10-29 04:51:07 +000026 addr |= (1 << 6);
Arthur Heymans70a8e342017-03-09 11:30:23 +010027 else
Angel Pons1d4044a2021-03-27 19:11:51 +010028 addr = ((u32)mchbar_read8(C0DRB3)) << 25;
Stefan Reinauer278534d2008-10-29 04:51:07 +000029 }
30
31 for (i = 0; i < 28; i++) {
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080032 read32((void *)addr);
33 read32((void *)(addr + 0x80));
Stefan Reinauer278534d2008-10-29 04:51:07 +000034 }
35
Angel Pons1d4044a2021-03-27 19:11:51 +010036 reg32 = mchbar_read32(RCVENMT);
Arthur Heymans70a8e342017-03-09 11:30:23 +010037 if (channel_offset == 0)
Stefan Reinauer278534d2008-10-29 04:51:07 +000038 reg32 = reg32 << 2;
Stefan Reinauer278534d2008-10-29 04:51:07 +000039
40 /**
41 * [19] = 1: all bits are high
42 * [18] = 1: all bits are low
43 * [19:18] = 00: bits are mixed high, low
44 */
45 return reg32;
46}
47
48/**
49 * This function sets receive enable coarse and medium timing parameters
50 */
51
52static void set_receive_enable(int channel_offset, u8 medium, u8 coarse)
53{
54 u32 reg32;
55
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010056 printk(BIOS_SPEW, " %s() medium=0x%x, coarse=0x%x\n", __func__, medium, coarse);
Stefan Reinauer71a3d962009-07-21 21:44:24 +000057
Angel Pons1d4044a2021-03-27 19:11:51 +010058 reg32 = mchbar_read32(C0DRT1 + channel_offset);
Stefan Reinauer278534d2008-10-29 04:51:07 +000059 reg32 &= 0xf0ffffff;
60 reg32 |= ((u32)coarse & 0x0f) << 24;
Angel Pons1d4044a2021-03-27 19:11:51 +010061 mchbar_write32(C0DRT1 + channel_offset, reg32);
Stefan Reinauer71a3d962009-07-21 21:44:24 +000062
63 /* This should never happen: */
Stefan Reinauer278534d2008-10-29 04:51:07 +000064 if (coarse > 0x0f)
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010065 printk(BIOS_DEBUG, "%s: coarse overflow: 0x%02x.\n", __func__, coarse);
Stefan Reinauer278534d2008-10-29 04:51:07 +000066
67 /* medium control
68 *
69 * 00 - 1/4 clock
70 * 01 - 1/2 clock
71 * 10 - 3/4 clock
72 * 11 - 1 clock
73 */
74
Angel Pons1d4044a2021-03-27 19:11:51 +010075 reg32 = mchbar_read32(RCVENMT);
Stefan Reinauer278534d2008-10-29 04:51:07 +000076 if (!channel_offset) {
Stefan Reinauer71a3d962009-07-21 21:44:24 +000077 /* Channel 0 */
Stefan Reinauer278534d2008-10-29 04:51:07 +000078 reg32 &= ~(3 << 2);
79 reg32 |= medium << 2;
80 } else {
Stefan Reinauer71a3d962009-07-21 21:44:24 +000081 /* Channel 1 */
Stefan Reinauer278534d2008-10-29 04:51:07 +000082 reg32 &= ~(3 << 0);
83 reg32 |= medium;
84 }
Angel Pons1d4044a2021-03-27 19:11:51 +010085 mchbar_write32(RCVENMT, reg32);
Stefan Reinauer278534d2008-10-29 04:51:07 +000086
87}
88
Arthur Heymans70a8e342017-03-09 11:30:23 +010089static int normalize(int channel_offset, u8 *mediumcoarse, u8 *fine)
Stefan Reinauer278534d2008-10-29 04:51:07 +000090{
Elyes HAOUAS3cd43272020-03-05 22:01:17 +010091 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +000092
93 if (*fine < 0x80)
94 return 0;
95
96 *fine -= 0x80;
97 *mediumcoarse += 1;
98
99 if (*mediumcoarse >= 0x40) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000100 printk(BIOS_DEBUG, "Normalize Error\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000101 return -1;
102 }
103
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200104 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000105
Angel Pons1d4044a2021-03-27 19:11:51 +0100106 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000107
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
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200126 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000127
128 reg32 = sample_strobes(channel_offset, sysinfo);
129
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000130 } while (reg32 & (1 << 19));
Stefan Reinauer278534d2008-10-29 04:51:07 +0000131
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000132 if (!(reg32 & (1 << 18))) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000133 printk(BIOS_DEBUG, "No Preamble found (neither high nor low).\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000134 return -1;
135 }
136
137 return 0;
138}
139
140/**
141 * add a quarter clock to the current receive enable settings
142 */
143
Arthur Heymans70a8e342017-03-09 11:30:23 +0100144static int add_quarter_clock(int channel_offset, u8 *mediumcoarse, u8 *fine)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000145{
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100146 printk(BIOS_SPEW, " %s() mediumcoarse=%02x fine=%02x\n", __func__,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000147 *mediumcoarse, *fine);
148 if (*fine >= 0x80) {
149 *fine -= 0x80;
150
151 *mediumcoarse += 2;
152 if (*mediumcoarse >= 0x40) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000153 printk(BIOS_DEBUG, "clocks at max.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000154 return -1;
155 }
156
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200157 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000158 } else {
159 *fine += 0x80;
160 }
161
Angel Pons1d4044a2021-03-27 19:11:51 +0100162 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000163
164 return 0;
165}
166
Arthur Heymans70a8e342017-03-09 11:30:23 +0100167static int find_strobes_low(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000168 struct sys_info *sysinfo)
169{
170 u32 rcvenmt;
171
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100172 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000173
Stefan Reinauer278534d2008-10-29 04:51:07 +0000174 for (;;) {
Angel Pons1d4044a2021-03-27 19:11:51 +0100175 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000176
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200177 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000178
179 rcvenmt = sample_strobes(channel_offset, sysinfo);
180
181 if (((rcvenmt & (1 << 18)) != 0))
182 return 0;
183
Stefan Reinauer278534d2008-10-29 04:51:07 +0000184 *fine -= 0x80;
185 if (*fine == 0)
186 continue;
187
188 *mediumcoarse -= 2;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000189 if (*mediumcoarse < 0xfe)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000190 continue;
191
192 break;
193
194 }
195
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000196 printk(BIOS_DEBUG, "Could not find low strobe\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000197 return 0;
198}
199
Arthur Heymans70a8e342017-03-09 11:30:23 +0100200static int find_strobes_edge(int channel_offset, u8 *mediumcoarse, u8 *fine,
Stefan Reinauer278534d2008-10-29 04:51:07 +0000201 struct sys_info *sysinfo)
202{
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000203
Stefan Reinauer278534d2008-10-29 04:51:07 +0000204 int counter;
205 u32 rcvenmt;
206
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100207 printk(BIOS_SPEW, " %s()\n", __func__);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000208
209 counter = 8;
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200210 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000211
212 for (;;) {
Angel Pons1d4044a2021-03-27 19:11:51 +0100213 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000214 rcvenmt = sample_strobes(channel_offset, sysinfo);
215
216 if ((rcvenmt & (1 << 19)) == 0) {
217 counter = 8;
218 } else {
219 counter--;
220 if (!counter)
221 break;
222 }
223
224 *fine = *fine + 1;
225 if (*fine < 0xf8) {
226 if (*fine & (1 << 3)) {
227 *fine &= ~(1 << 3);
228 *fine += 0x10;
229 }
230 continue;
231 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000232
Stefan Reinauer278534d2008-10-29 04:51:07 +0000233 *fine = 0;
234 *mediumcoarse += 2;
235 if (*mediumcoarse <= 0x40) {
236 set_receive_enable(channel_offset, *mediumcoarse & 3,
237 *mediumcoarse >> 2);
238 continue;
239 }
240
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000241 printk(BIOS_DEBUG, "Could not find rising edge.\n");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000242 return -1;
243 }
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000244
Stefan Reinauer278534d2008-10-29 04:51:07 +0000245 *fine -= 7;
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000246 if (*fine >= 0xf9) {
Stefan Reinauer278534d2008-10-29 04:51:07 +0000247 *mediumcoarse -= 2;
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200248 set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000249 }
250
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000251 *fine &= ~(1 << 3);
Angel Pons1d4044a2021-03-27 19:11:51 +0100252 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000253
254 return 0;
255}
256
257/**
Angel Pons22aeed32020-06-11 13:28:03 +0200258 * Here we use a trick. The RCVEN channel 1 registers are all at an
Stefan Reinauer278534d2008-10-29 04:51:07 +0000259 * offset of 0x80 to the channel 0 registers. We don't want to waste
Elyes HAOUAS12df9502016-08-23 21:29:48 +0200260 * a lot of if ()s so let's just pass 0 or 0x80 for the channel offset.
Stefan Reinauer278534d2008-10-29 04:51:07 +0000261 */
262
Elyes HAOUAS3dff32c2020-03-30 17:16:51 +0200263static int receive_enable_autoconfig(int channel_offset, struct sys_info *sysinfo)
Stefan Reinauer278534d2008-10-29 04:51:07 +0000264{
265 u8 mediumcoarse;
266 u8 fine;
267
Elyes HAOUAS3cd43272020-03-05 22:01:17 +0100268 printk(BIOS_SPEW, "%s() for channel %d\n", __func__, channel_offset ? 1 : 0);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000269
270 /* Set initial values */
Stefan Reinauer71a3d962009-07-21 21:44:24 +0000271 mediumcoarse = (sysinfo->cas << 2) | 3;
Stefan Reinauer278534d2008-10-29 04:51:07 +0000272 fine = 0;
273
274 if (find_strobes_low(channel_offset, &mediumcoarse, &fine, sysinfo))
275 return -1;
276
277 if (find_strobes_edge(channel_offset, &mediumcoarse, &fine, sysinfo))
278 return -1;
279
280 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
281 return -1;
282
283 if (find_preamble(channel_offset, &mediumcoarse, sysinfo))
284 return -1;
285
286 if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
287 return -1;
288
289 if (normalize(channel_offset, &mediumcoarse, &fine))
290 return -1;
291
292 /* This is a debug check to see if the rcven code is fully working.
293 * It can be removed when the output message is not printed anymore
294 */
Angel Pons1d4044a2021-03-27 19:11:51 +0100295 if (mchbar_read8(C0WL0REOST + channel_offset) == 0)
Angel Pons30492572020-06-11 13:24:54 +0200296 printk(BIOS_DEBUG, "Weird. No C%sWL0REOST\n", channel_offset ? "1" : "0");
Stefan Reinauer278534d2008-10-29 04:51:07 +0000297
298 return 0;
299}
300
301void receive_enable_adjust(struct sys_info *sysinfo)
302{
303 /* Is channel 0 populated? */
304 if (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED
305 || sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED)
306 if (receive_enable_autoconfig(0, sysinfo))
307 return;
308
309 /* Is channel 1 populated? */
310 if (sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED
311 || sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED)
312 if (receive_enable_autoconfig(0x80, sysinfo))
313 return;
314}