xref: /aosp_15_r20/external/coreboot/src/northbridge/intel/i945/rcven.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <console/console.h>
4 #include <device/mmio.h>
5 #include <types.h>
6 
7 #include "i945.h"
8 #include "raminit.h"
9 
10 /**
11  * sample the strobes signal
12  */
sample_strobes(int channel_offset,struct sys_info * sysinfo)13 static u32 sample_strobes(int channel_offset, struct sys_info *sysinfo)
14 {
15 	u32 reg32;
16 	uintptr_t addr;
17 	int i;
18 
19 	mchbar_setbits32(C0DRC1 + channel_offset, 1 << 6);
20 	mchbar_clrbits32(C0DRC1 + channel_offset, 1 << 6);
21 
22 	addr = 0;
23 
24 	if (channel_offset != 0) {	/* must be dual channel */
25 		if (sysinfo->interleaved == 1)
26 			addr |= (1 << 6);
27 		else
28 			addr = ((u32)mchbar_read8(C0DRB3)) << 25;
29 	}
30 
31 	for (i = 0; i < 28; i++) {
32 		read32p(addr);
33 		read32p(addr + 0x80);
34 	}
35 
36 	reg32 = mchbar_read32(RCVENMT);
37 	if (channel_offset == 0)
38 		reg32 = reg32 << 2;
39 
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 
set_receive_enable(int channel_offset,u8 medium,u8 coarse)52 static void set_receive_enable(int channel_offset, u8 medium, u8 coarse)
53 {
54 	u32 reg32;
55 
56 	printk(BIOS_SPEW, "    %s() medium=0x%x, coarse=0x%x\n", __func__, medium, coarse);
57 
58 	reg32 = mchbar_read32(C0DRT1 + channel_offset);
59 	reg32 &= 0xf0ffffff;
60 	reg32 |= ((u32)coarse & 0x0f) << 24;
61 	mchbar_write32(C0DRT1 + channel_offset, reg32);
62 
63 	/* This should never happen: */
64 	if (coarse > 0x0f)
65 		printk(BIOS_DEBUG, "%s: coarse overflow: 0x%02x.\n", __func__, coarse);
66 
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 
75 	reg32 = mchbar_read32(RCVENMT);
76 	if (!channel_offset) {
77 		/* Channel 0 */
78 		reg32 &= ~(3 << 2);
79 		reg32 |= medium << 2;
80 	} else {
81 		/* Channel 1 */
82 		reg32 &= ~(3 << 0);
83 		reg32 |= medium;
84 	}
85 	mchbar_write32(RCVENMT, reg32);
86 }
87 
normalize(int channel_offset,u8 * mediumcoarse,u8 * fine)88 static int normalize(int channel_offset, u8 *mediumcoarse, u8 *fine)
89 {
90 	printk(BIOS_SPEW, "  %s()\n", __func__);
91 
92 	if (*fine < 0x80)
93 		return 0;
94 
95 	*fine -= 0x80;
96 	*mediumcoarse += 1;
97 
98 	if (*mediumcoarse >= 0x40) {
99 		printk(BIOS_DEBUG, "Normalize Error\n");
100 		return -1;
101 	}
102 
103 	set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
104 
105 	mchbar_write8(C0WL0REOST + channel_offset, *fine);
106 
107 	return 0;
108 }
109 
find_preamble(int channel_offset,u8 * mediumcoarse,struct sys_info * sysinfo)110 static int find_preamble(int channel_offset, u8 *mediumcoarse,
111 			 struct sys_info *sysinfo)
112 {
113 	/* find start of the data phase */
114 	u32 reg32;
115 
116 	printk(BIOS_SPEW, "  %s()\n", __func__);
117 
118 	do {
119 		if (*mediumcoarse < 4) {
120 			printk(BIOS_DEBUG, "No Preamble found.\n");
121 			return -1;
122 		}
123 		*mediumcoarse -= 4;
124 
125 		set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
126 
127 		reg32 = sample_strobes(channel_offset, sysinfo);
128 
129 	} while (reg32 & (1 << 19));
130 
131 	if (!(reg32 & (1 << 18))) {
132 		printk(BIOS_DEBUG, "No Preamble found (neither high nor low).\n");
133 		return -1;
134 	}
135 
136 	return 0;
137 }
138 
139 /**
140  * add a quarter clock to the current receive enable settings
141  */
142 
add_quarter_clock(int channel_offset,u8 * mediumcoarse,u8 * fine)143 static int add_quarter_clock(int channel_offset, u8 *mediumcoarse, u8 *fine)
144 {
145 	printk(BIOS_SPEW, "  %s() mediumcoarse=%02x fine=%02x\n", __func__,
146 			*mediumcoarse, *fine);
147 	if (*fine >= 0x80) {
148 		*fine -= 0x80;
149 
150 		*mediumcoarse += 2;
151 		if (*mediumcoarse >= 0x40) {
152 			printk(BIOS_DEBUG, "clocks at max.\n");
153 			return -1;
154 		}
155 
156 		set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
157 	} else {
158 		*fine += 0x80;
159 	}
160 
161 	mchbar_write8(C0WL0REOST + channel_offset, *fine);
162 
163 	return 0;
164 }
165 
find_strobes_low(int channel_offset,u8 * mediumcoarse,u8 * fine,struct sys_info * sysinfo)166 static int find_strobes_low(int channel_offset, u8 *mediumcoarse, u8 *fine,
167 			    struct sys_info *sysinfo)
168 {
169 	u32 rcvenmt;
170 
171 	printk(BIOS_SPEW, "  %s()\n", __func__);
172 
173 	for (;;) {
174 		mchbar_write8(C0WL0REOST + channel_offset, *fine);
175 
176 		set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
177 
178 		rcvenmt = sample_strobes(channel_offset, sysinfo);
179 
180 		if (((rcvenmt & (1 << 18)) != 0))
181 			return 0;
182 
183 		*fine -= 0x80;
184 		if (*fine == 0)
185 			continue;
186 
187 		*mediumcoarse -= 2;
188 		if (*mediumcoarse < 0xfe)
189 			continue;
190 
191 		break;
192 	}
193 
194 	printk(BIOS_DEBUG, "Could not find low strobe\n");
195 	return 0;
196 }
197 
find_strobes_edge(int channel_offset,u8 * mediumcoarse,u8 * fine,struct sys_info * sysinfo)198 static int find_strobes_edge(int channel_offset, u8 *mediumcoarse, u8 *fine,
199 			     struct sys_info *sysinfo)
200 {
201 	int counter;
202 	u32 rcvenmt;
203 
204 	printk(BIOS_SPEW, "  %s()\n", __func__);
205 
206 	counter = 8;
207 	set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
208 
209 	for (;;) {
210 		mchbar_write8(C0WL0REOST + channel_offset, *fine);
211 		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 		}
229 
230 		*fine = 0;
231 		*mediumcoarse += 2;
232 		if (*mediumcoarse <= 0x40) {
233 			set_receive_enable(channel_offset, *mediumcoarse & 3,
234 					   *mediumcoarse >> 2);
235 			continue;
236 		}
237 
238 		printk(BIOS_DEBUG, "Could not find rising edge.\n");
239 		return -1;
240 	}
241 
242 	*fine -= 7;
243 	if (*fine >= 0xf9) {
244 		*mediumcoarse -= 2;
245 		set_receive_enable(channel_offset, *mediumcoarse & 3, *mediumcoarse >> 2);
246 	}
247 
248 	*fine &= ~(1 << 3);
249 	mchbar_write8(C0WL0REOST + channel_offset, *fine);
250 
251 	return 0;
252 }
253 
254 /**
255  * Here we use a trick. The RCVEN channel 1 registers are all at an
256  * offset of 0x80 to the channel 0 registers. We don't want to waste
257  * a lot of if ()s so let's just pass 0 or 0x80 for the channel offset.
258  */
259 
receive_enable_autoconfig(int channel_offset,struct sys_info * sysinfo)260 static int receive_enable_autoconfig(int channel_offset, struct sys_info *sysinfo)
261 {
262 	u8 mediumcoarse;
263 	u8 fine;
264 
265 	printk(BIOS_SPEW, "%s() for channel %d\n", __func__, channel_offset ? 1 : 0);
266 
267 	/* Set initial values */
268 	mediumcoarse = (sysinfo->cas << 2) | 3;
269 	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 	 */
292 	if (mchbar_read8(C0WL0REOST + channel_offset) == 0)
293 		printk(BIOS_DEBUG, "Weird. No C%sWL0REOST\n", channel_offset ? "1" : "0");
294 
295 	return 0;
296 }
297 
receive_enable_adjust(struct sys_info * sysinfo)298 void 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 }
312