blob: 024a2630eb7e7bdfc1d27aa3cda651f4eba0cf53 [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++) {
Elyes Haouas60bdb322022-10-03 15:40:00 +020032 read32p(addr);
33 read32p(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
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
Angel Pons1d4044a2021-03-27 19:11:51 +0100105 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000106
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
Angel Pons1d4044a2021-03-27 19:11:51 +0100161 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000162
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 (;;) {
Angel Pons1d4044a2021-03-27 19:11:51 +0100174 mchbar_write8(C0WL0REOST + channel_offset, *fine);
Stefan Reinauer278534d2008-10-29 04:51:07 +0000175
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;
Stefan Reinauer278534d2008-10-29 04:51:07 +0000192 }
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{
201 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}