xref: /openbmc/u-boot/arch/x86/cpu/quark/mrc_util.c (revision 85231c08)
1 /*
2  * Copyright (C) 2013, Intel Corporation
3  * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
4  *
5  * Ported from Intel released Quark UEFI BIOS
6  * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei
7  *
8  * SPDX-License-Identifier:	Intel
9  */
10 
11 #include <common.h>
12 #include <asm/arch/device.h>
13 #include <asm/arch/mrc.h>
14 #include <asm/arch/msg_port.h>
15 #include <asm/arch/quark.h>
16 #include "mrc_util.h"
17 #include "hte.h"
18 #include "smc.h"
19 
20 static const uint8_t vref_codes[64] = {
21 	/* lowest to highest */
22 	0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38,
23 	0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
24 	0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28,
25 	0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
26 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
27 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
28 	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
29 	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
30 };
31 
32 void mrc_write_mask(u32 unit, u32 addr, u32 data, u32 mask)
33 {
34 	msg_port_write(unit, addr,
35 		       (msg_port_read(unit, addr) & ~(mask)) |
36 		       ((data) & (mask)));
37 }
38 
39 void mrc_alt_write_mask(u32 unit, u32 addr, u32 data, u32 mask)
40 {
41 	msg_port_alt_write(unit, addr,
42 			   (msg_port_alt_read(unit, addr) & ~(mask)) |
43 			   ((data) & (mask)));
44 }
45 
46 void mrc_post_code(uint8_t major, uint8_t minor)
47 {
48 	/* send message to UART */
49 	DPF(D_INFO, "POST: 0x%01x%02x\n", major, minor);
50 
51 	/* error check */
52 	if (major == 0xee)
53 		hang();
54 }
55 
56 /* Delay number of nanoseconds */
57 void delay_n(uint32_t ns)
58 {
59 	/* 1000 MHz clock has 1ns period --> no conversion required */
60 	uint64_t final_tsc = rdtsc();
61 
62 	final_tsc += ((get_tbclk_mhz() * ns) / 1000);
63 
64 	while (rdtsc() < final_tsc)
65 		;
66 }
67 
68 /* Delay number of microseconds */
69 void delay_u(uint32_t ms)
70 {
71 	/* 64-bit math is not an option, just use loops */
72 	while (ms--)
73 		delay_n(1000);
74 }
75 
76 /* Select Memory Manager as the source for PRI interface */
77 void select_mem_mgr(void)
78 {
79 	u32 dco;
80 
81 	ENTERFN();
82 
83 	dco = msg_port_read(MEM_CTLR, DCO);
84 	dco &= ~DCO_PMICTL;
85 	msg_port_write(MEM_CTLR, DCO, dco);
86 
87 	LEAVEFN();
88 }
89 
90 /* Select HTE as the source for PRI interface */
91 void select_hte(void)
92 {
93 	u32 dco;
94 
95 	ENTERFN();
96 
97 	dco = msg_port_read(MEM_CTLR, DCO);
98 	dco |= DCO_PMICTL;
99 	msg_port_write(MEM_CTLR, DCO, dco);
100 
101 	LEAVEFN();
102 }
103 
104 /*
105  * Send DRAM command
106  * data should be formated using DCMD_Xxxx macro or emrsXCommand structure
107  */
108 void dram_init_command(uint32_t data)
109 {
110 	qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, data);
111 	qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG, 0);
112 	msg_port_setup(MSG_OP_DRAM_INIT, MEM_CTLR, 0);
113 
114 	DPF(D_REGWR, "WR32 %03X %08X %08X\n", MEM_CTLR, 0, data);
115 }
116 
117 /* Send DRAM wake command using special MCU side-band WAKE opcode */
118 void dram_wake_command(void)
119 {
120 	ENTERFN();
121 
122 	msg_port_setup(MSG_OP_DRAM_WAKE, MEM_CTLR, 0);
123 
124 	LEAVEFN();
125 }
126 
127 void training_message(uint8_t channel, uint8_t rank, uint8_t byte_lane)
128 {
129 	/* send message to UART */
130 	DPF(D_INFO, "CH%01X RK%01X BL%01X\n", channel, rank, byte_lane);
131 }
132 
133 /*
134  * This function will program the RCVEN delays
135  *
136  * (currently doesn't comprehend rank)
137  */
138 void set_rcvn(uint8_t channel, uint8_t rank,
139 	      uint8_t byte_lane, uint32_t pi_count)
140 {
141 	uint32_t reg;
142 	uint32_t msk;
143 	uint32_t temp;
144 
145 	ENTERFN();
146 
147 	DPF(D_TRN, "Rcvn ch%d rnk%d ln%d : pi=%03X\n",
148 	    channel, rank, byte_lane, pi_count);
149 
150 	/*
151 	 * RDPTR (1/2 MCLK, 64 PIs)
152 	 * BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
153 	 * BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
154 	 */
155 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
156 		channel * DDRIODQ_CH_OFFSET;
157 	msk = (byte_lane & 1) ? 0xf00000 : 0xf00;
158 	temp = (byte_lane & 1) ? (pi_count / HALF_CLK) << 20 :
159 		(pi_count / HALF_CLK) << 8;
160 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
161 
162 	/* Adjust PI_COUNT */
163 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
164 
165 	/*
166 	 * PI (1/64 MCLK, 1 PIs)
167 	 * BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
168 	 * BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
169 	 */
170 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
171 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
172 		channel * DDRIODQ_CH_OFFSET);
173 	msk = 0x3f000000;
174 	temp = pi_count << 24;
175 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
176 
177 	/*
178 	 * DEADBAND
179 	 * BL0/1 -> B01DBCTL1[08/11] (+1 select)
180 	 * BL0/1 -> B01DBCTL1[02/05] (enable)
181 	 */
182 	reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
183 		channel * DDRIODQ_CH_OFFSET;
184 	msk = 0x00;
185 	temp = 0x00;
186 
187 	/* enable */
188 	msk |= (byte_lane & 1) ? (1 << 5) : (1 << 2);
189 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
190 		temp |= msk;
191 
192 	/* select */
193 	msk |= (byte_lane & 1) ? (1 << 11) : (1 << 8);
194 	if (pi_count < EARLY_DB)
195 		temp |= msk;
196 
197 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
198 
199 	/* error check */
200 	if (pi_count > 0x3f) {
201 		training_message(channel, rank, byte_lane);
202 		mrc_post_code(0xee, 0xe0);
203 	}
204 
205 	LEAVEFN();
206 }
207 
208 /*
209  * This function will return the current RCVEN delay on the given
210  * channel, rank, byte_lane as an absolute PI count.
211  *
212  * (currently doesn't comprehend rank)
213  */
214 uint32_t get_rcvn(uint8_t channel, uint8_t rank, uint8_t byte_lane)
215 {
216 	uint32_t reg;
217 	uint32_t temp;
218 	uint32_t pi_count;
219 
220 	ENTERFN();
221 
222 	/*
223 	 * RDPTR (1/2 MCLK, 64 PIs)
224 	 * BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
225 	 * BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
226 	 */
227 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
228 		channel * DDRIODQ_CH_OFFSET;
229 	temp = msg_port_alt_read(DDRPHY, reg);
230 	temp >>= (byte_lane & 1) ? 20 : 8;
231 	temp &= 0xf;
232 
233 	/* Adjust PI_COUNT */
234 	pi_count = temp * HALF_CLK;
235 
236 	/*
237 	 * PI (1/64 MCLK, 1 PIs)
238 	 * BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
239 	 * BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
240 	 */
241 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
242 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
243 		channel * DDRIODQ_CH_OFFSET);
244 	temp = msg_port_alt_read(DDRPHY, reg);
245 	temp >>= 24;
246 	temp &= 0x3f;
247 
248 	/* Adjust PI_COUNT */
249 	pi_count += temp;
250 
251 	LEAVEFN();
252 
253 	return pi_count;
254 }
255 
256 /*
257  * This function will program the RDQS delays based on an absolute
258  * amount of PIs.
259  *
260  * (currently doesn't comprehend rank)
261  */
262 void set_rdqs(uint8_t channel, uint8_t rank,
263 	      uint8_t byte_lane, uint32_t pi_count)
264 {
265 	uint32_t reg;
266 	uint32_t msk;
267 	uint32_t temp;
268 
269 	ENTERFN();
270 	DPF(D_TRN, "Rdqs ch%d rnk%d ln%d : pi=%03X\n",
271 	    channel, rank, byte_lane, pi_count);
272 
273 	/*
274 	 * PI (1/128 MCLK)
275 	 * BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
276 	 * BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
277 	 */
278 	reg = (byte_lane & 1) ? B1RXDQSPICODE : B0RXDQSPICODE;
279 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
280 		channel * DDRIODQ_CH_OFFSET);
281 	msk = 0x7f;
282 	temp = pi_count << 0;
283 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
284 
285 	/* error check (shouldn't go above 0x3F) */
286 	if (pi_count > 0x47) {
287 		training_message(channel, rank, byte_lane);
288 		mrc_post_code(0xee, 0xe1);
289 	}
290 
291 	LEAVEFN();
292 }
293 
294 /*
295  * This function will return the current RDQS delay on the given
296  * channel, rank, byte_lane as an absolute PI count.
297  *
298  * (currently doesn't comprehend rank)
299  */
300 uint32_t get_rdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane)
301 {
302 	uint32_t reg;
303 	uint32_t temp;
304 	uint32_t pi_count;
305 
306 	ENTERFN();
307 
308 	/*
309 	 * PI (1/128 MCLK)
310 	 * BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
311 	 * BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
312 	 */
313 	reg = (byte_lane & 1) ? B1RXDQSPICODE : B0RXDQSPICODE;
314 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
315 		channel * DDRIODQ_CH_OFFSET);
316 	temp = msg_port_alt_read(DDRPHY, reg);
317 
318 	/* Adjust PI_COUNT */
319 	pi_count = temp & 0x7f;
320 
321 	LEAVEFN();
322 
323 	return pi_count;
324 }
325 
326 /*
327  * This function will program the WDQS delays based on an absolute
328  * amount of PIs.
329  *
330  * (currently doesn't comprehend rank)
331  */
332 void set_wdqs(uint8_t channel, uint8_t rank,
333 	      uint8_t byte_lane, uint32_t pi_count)
334 {
335 	uint32_t reg;
336 	uint32_t msk;
337 	uint32_t temp;
338 
339 	ENTERFN();
340 
341 	DPF(D_TRN, "Wdqs ch%d rnk%d ln%d : pi=%03X\n",
342 	    channel, rank, byte_lane, pi_count);
343 
344 	/*
345 	 * RDPTR (1/2 MCLK, 64 PIs)
346 	 * BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
347 	 * BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
348 	 */
349 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
350 		channel * DDRIODQ_CH_OFFSET;
351 	msk = (byte_lane & 1) ? 0xf0000 : 0xf0;
352 	temp = pi_count / HALF_CLK;
353 	temp <<= (byte_lane & 1) ? 16 : 4;
354 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
355 
356 	/* Adjust PI_COUNT */
357 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
358 
359 	/*
360 	 * PI (1/64 MCLK, 1 PIs)
361 	 * BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
362 	 * BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
363 	 */
364 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
365 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
366 		channel * DDRIODQ_CH_OFFSET);
367 	msk = 0x3f0000;
368 	temp = pi_count << 16;
369 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
370 
371 	/*
372 	 * DEADBAND
373 	 * BL0/1 -> B01DBCTL1[07/10] (+1 select)
374 	 * BL0/1 -> B01DBCTL1[01/04] (enable)
375 	 */
376 	reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
377 		channel * DDRIODQ_CH_OFFSET;
378 	msk = 0x00;
379 	temp = 0x00;
380 
381 	/* enable */
382 	msk |= (byte_lane & 1) ? (1 << 4) : (1 << 1);
383 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
384 		temp |= msk;
385 
386 	/* select */
387 	msk |= (byte_lane & 1) ? (1 << 10) : (1 << 7);
388 	if (pi_count < EARLY_DB)
389 		temp |= msk;
390 
391 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
392 
393 	/* error check */
394 	if (pi_count > 0x3f) {
395 		training_message(channel, rank, byte_lane);
396 		mrc_post_code(0xee, 0xe2);
397 	}
398 
399 	LEAVEFN();
400 }
401 
402 /*
403  * This function will return the amount of WDQS delay on the given
404  * channel, rank, byte_lane as an absolute PI count.
405  *
406  * (currently doesn't comprehend rank)
407  */
408 uint32_t get_wdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane)
409 {
410 	uint32_t reg;
411 	uint32_t temp;
412 	uint32_t pi_count;
413 
414 	ENTERFN();
415 
416 	/*
417 	 * RDPTR (1/2 MCLK, 64 PIs)
418 	 * BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
419 	 * BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
420 	 */
421 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
422 		channel * DDRIODQ_CH_OFFSET;
423 	temp = msg_port_alt_read(DDRPHY, reg);
424 	temp >>= (byte_lane & 1) ? 16 : 4;
425 	temp &= 0xf;
426 
427 	/* Adjust PI_COUNT */
428 	pi_count = (temp * HALF_CLK);
429 
430 	/*
431 	 * PI (1/64 MCLK, 1 PIs)
432 	 * BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
433 	 * BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
434 	 */
435 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
436 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
437 		channel * DDRIODQ_CH_OFFSET);
438 	temp = msg_port_alt_read(DDRPHY, reg);
439 	temp >>= 16;
440 	temp &= 0x3f;
441 
442 	/* Adjust PI_COUNT */
443 	pi_count += temp;
444 
445 	LEAVEFN();
446 
447 	return pi_count;
448 }
449 
450 /*
451  * This function will program the WDQ delays based on an absolute
452  * number of PIs.
453  *
454  * (currently doesn't comprehend rank)
455  */
456 void set_wdq(uint8_t channel, uint8_t rank,
457 	     uint8_t byte_lane, uint32_t pi_count)
458 {
459 	uint32_t reg;
460 	uint32_t msk;
461 	uint32_t temp;
462 
463 	ENTERFN();
464 
465 	DPF(D_TRN, "Wdq ch%d rnk%d ln%d : pi=%03X\n",
466 	    channel, rank, byte_lane, pi_count);
467 
468 	/*
469 	 * RDPTR (1/2 MCLK, 64 PIs)
470 	 * BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
471 	 * BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
472 	 */
473 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
474 		channel * DDRIODQ_CH_OFFSET;
475 	msk = (byte_lane & 1) ? 0xf000 : 0xf;
476 	temp = pi_count / HALF_CLK;
477 	temp <<= (byte_lane & 1) ? 12 : 0;
478 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
479 
480 	/* Adjust PI_COUNT */
481 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
482 
483 	/*
484 	 * PI (1/64 MCLK, 1 PIs)
485 	 * BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
486 	 * BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
487 	 */
488 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
489 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
490 		channel * DDRIODQ_CH_OFFSET);
491 	msk = 0x3f00;
492 	temp = pi_count << 8;
493 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
494 
495 	/*
496 	 * DEADBAND
497 	 * BL0/1 -> B01DBCTL1[06/09] (+1 select)
498 	 * BL0/1 -> B01DBCTL1[00/03] (enable)
499 	 */
500 	reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
501 		channel * DDRIODQ_CH_OFFSET;
502 	msk = 0x00;
503 	temp = 0x00;
504 
505 	/* enable */
506 	msk |= (byte_lane & 1) ? (1 << 3) : (1 << 0);
507 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
508 		temp |= msk;
509 
510 	/* select */
511 	msk |= (byte_lane & 1) ? (1 << 9) : (1 << 6);
512 	if (pi_count < EARLY_DB)
513 		temp |= msk;
514 
515 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
516 
517 	/* error check */
518 	if (pi_count > 0x3f) {
519 		training_message(channel, rank, byte_lane);
520 		mrc_post_code(0xee, 0xe3);
521 	}
522 
523 	LEAVEFN();
524 }
525 
526 /*
527  * This function will return the amount of WDQ delay on the given
528  * channel, rank, byte_lane as an absolute PI count.
529  *
530  * (currently doesn't comprehend rank)
531  */
532 uint32_t get_wdq(uint8_t channel, uint8_t rank, uint8_t byte_lane)
533 {
534 	uint32_t reg;
535 	uint32_t temp;
536 	uint32_t pi_count;
537 
538 	ENTERFN();
539 
540 	/*
541 	 * RDPTR (1/2 MCLK, 64 PIs)
542 	 * BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
543 	 * BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
544 	 */
545 	reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET +
546 		channel * DDRIODQ_CH_OFFSET;
547 	temp = msg_port_alt_read(DDRPHY, reg);
548 	temp >>= (byte_lane & 1) ? 12 : 0;
549 	temp &= 0xf;
550 
551 	/* Adjust PI_COUNT */
552 	pi_count = temp * HALF_CLK;
553 
554 	/*
555 	 * PI (1/64 MCLK, 1 PIs)
556 	 * BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
557 	 * BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
558 	 */
559 	reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0;
560 	reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET +
561 		channel * DDRIODQ_CH_OFFSET);
562 	temp = msg_port_alt_read(DDRPHY, reg);
563 	temp >>= 8;
564 	temp &= 0x3f;
565 
566 	/* Adjust PI_COUNT */
567 	pi_count += temp;
568 
569 	LEAVEFN();
570 
571 	return pi_count;
572 }
573 
574 /*
575  * This function will program the WCMD delays based on an absolute
576  * number of PIs.
577  */
578 void set_wcmd(uint8_t channel, uint32_t pi_count)
579 {
580 	uint32_t reg;
581 	uint32_t msk;
582 	uint32_t temp;
583 
584 	ENTERFN();
585 
586 	/*
587 	 * RDPTR (1/2 MCLK, 64 PIs)
588 	 * CMDPTRREG[11:08] (0x0-0xF)
589 	 */
590 	reg = CMDPTRREG + channel * DDRIOCCC_CH_OFFSET;
591 	msk = 0xf00;
592 	temp = pi_count / HALF_CLK;
593 	temp <<= 8;
594 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
595 
596 	/* Adjust PI_COUNT */
597 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
598 
599 	/*
600 	 * PI (1/64 MCLK, 1 PIs)
601 	 * CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
602 	 * CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
603 	 * CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
604 	 * CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
605 	 * CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
606 	 * CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
607 	 * CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
608 	 * CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
609 	 */
610 	reg = CMDDLLPICODER1 + channel * DDRIOCCC_CH_OFFSET;
611 	msk = 0x3f3f3f3f;
612 	temp = (pi_count << 24) | (pi_count << 16) |
613 		(pi_count << 8) | (pi_count << 0);
614 
615 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
616 	reg = CMDDLLPICODER0 + channel * DDRIOCCC_CH_OFFSET;	/* PO */
617 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
618 
619 	/*
620 	 * DEADBAND
621 	 * CMDCFGREG0[17] (+1 select)
622 	 * CMDCFGREG0[16] (enable)
623 	 */
624 	reg = CMDCFGREG0 + channel * DDRIOCCC_CH_OFFSET;
625 	msk = 0x00;
626 	temp = 0x00;
627 
628 	/* enable */
629 	msk |= (1 << 16);
630 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
631 		temp |= msk;
632 
633 	/* select */
634 	msk |= (1 << 17);
635 	if (pi_count < EARLY_DB)
636 		temp |= msk;
637 
638 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
639 
640 	/* error check */
641 	if (pi_count > 0x3f)
642 		mrc_post_code(0xee, 0xe4);
643 
644 	LEAVEFN();
645 }
646 
647 /*
648  * This function will return the amount of WCMD delay on the given
649  * channel as an absolute PI count.
650  */
651 uint32_t get_wcmd(uint8_t channel)
652 {
653 	uint32_t reg;
654 	uint32_t temp;
655 	uint32_t pi_count;
656 
657 	ENTERFN();
658 
659 	/*
660 	 * RDPTR (1/2 MCLK, 64 PIs)
661 	 * CMDPTRREG[11:08] (0x0-0xF)
662 	 */
663 	reg = CMDPTRREG + channel * DDRIOCCC_CH_OFFSET;
664 	temp = msg_port_alt_read(DDRPHY, reg);
665 	temp >>= 8;
666 	temp &= 0xf;
667 
668 	/* Adjust PI_COUNT */
669 	pi_count = temp * HALF_CLK;
670 
671 	/*
672 	 * PI (1/64 MCLK, 1 PIs)
673 	 * CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
674 	 * CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
675 	 * CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
676 	 * CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
677 	 * CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
678 	 * CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
679 	 * CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
680 	 * CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
681 	 */
682 	reg = CMDDLLPICODER1 + channel * DDRIOCCC_CH_OFFSET;
683 	temp = msg_port_alt_read(DDRPHY, reg);
684 	temp >>= 16;
685 	temp &= 0x3f;
686 
687 	/* Adjust PI_COUNT */
688 	pi_count += temp;
689 
690 	LEAVEFN();
691 
692 	return pi_count;
693 }
694 
695 /*
696  * This function will program the WCLK delays based on an absolute
697  * number of PIs.
698  */
699 void set_wclk(uint8_t channel, uint8_t rank, uint32_t pi_count)
700 {
701 	uint32_t reg;
702 	uint32_t msk;
703 	uint32_t temp;
704 
705 	ENTERFN();
706 
707 	/*
708 	 * RDPTR (1/2 MCLK, 64 PIs)
709 	 * CCPTRREG[15:12] -> CLK1 (0x0-0xF)
710 	 * CCPTRREG[11:08] -> CLK0 (0x0-0xF)
711 	 */
712 	reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET;
713 	msk = 0xff00;
714 	temp = ((pi_count / HALF_CLK) << 12) | ((pi_count / HALF_CLK) << 8);
715 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
716 
717 	/* Adjust PI_COUNT */
718 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
719 
720 	/*
721 	 * PI (1/64 MCLK, 1 PIs)
722 	 * ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
723 	 * ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
724 	 */
725 	reg = rank ? ECCB1DLLPICODER0 : ECCB1DLLPICODER0;
726 	reg += (channel * DDRIOCCC_CH_OFFSET);
727 	msk = 0x3f3f00;
728 	temp = (pi_count << 16) | (pi_count << 8);
729 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
730 
731 	reg = rank ? ECCB1DLLPICODER1 : ECCB1DLLPICODER1;
732 	reg += (channel * DDRIOCCC_CH_OFFSET);
733 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
734 
735 	reg = rank ? ECCB1DLLPICODER2 : ECCB1DLLPICODER2;
736 	reg += (channel * DDRIOCCC_CH_OFFSET);
737 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
738 
739 	reg = rank ? ECCB1DLLPICODER3 : ECCB1DLLPICODER3;
740 	reg += (channel * DDRIOCCC_CH_OFFSET);
741 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
742 
743 	/*
744 	 * DEADBAND
745 	 * CCCFGREG1[11:08] (+1 select)
746 	 * CCCFGREG1[03:00] (enable)
747 	 */
748 	reg = CCCFGREG1 + channel * DDRIOCCC_CH_OFFSET;
749 	msk = 0x00;
750 	temp = 0x00;
751 
752 	/* enable */
753 	msk |= 0xf;
754 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
755 		temp |= msk;
756 
757 	/* select */
758 	msk |= 0xf00;
759 	if (pi_count < EARLY_DB)
760 		temp |= msk;
761 
762 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
763 
764 	/* error check */
765 	if (pi_count > 0x3f)
766 		mrc_post_code(0xee, 0xe5);
767 
768 	LEAVEFN();
769 }
770 
771 /*
772  * This function will return the amout of WCLK delay on the given
773  * channel, rank as an absolute PI count.
774  */
775 uint32_t get_wclk(uint8_t channel, uint8_t rank)
776 {
777 	uint32_t reg;
778 	uint32_t temp;
779 	uint32_t pi_count;
780 
781 	ENTERFN();
782 
783 	/*
784 	 * RDPTR (1/2 MCLK, 64 PIs)
785 	 * CCPTRREG[15:12] -> CLK1 (0x0-0xF)
786 	 * CCPTRREG[11:08] -> CLK0 (0x0-0xF)
787 	 */
788 	reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET;
789 	temp = msg_port_alt_read(DDRPHY, reg);
790 	temp >>= rank ? 12 : 8;
791 	temp &= 0xf;
792 
793 	/* Adjust PI_COUNT */
794 	pi_count = temp * HALF_CLK;
795 
796 	/*
797 	 * PI (1/64 MCLK, 1 PIs)
798 	 * ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
799 	 * ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
800 	 */
801 	reg = rank ? ECCB1DLLPICODER0 : ECCB1DLLPICODER0;
802 	reg += (channel * DDRIOCCC_CH_OFFSET);
803 	temp = msg_port_alt_read(DDRPHY, reg);
804 	temp >>= rank ? 16 : 8;
805 	temp &= 0x3f;
806 
807 	pi_count += temp;
808 
809 	LEAVEFN();
810 
811 	return pi_count;
812 }
813 
814 /*
815  * This function will program the WCTL delays based on an absolute
816  * number of PIs.
817  *
818  * (currently doesn't comprehend rank)
819  */
820 void set_wctl(uint8_t channel, uint8_t rank, uint32_t pi_count)
821 {
822 	uint32_t reg;
823 	uint32_t msk;
824 	uint32_t temp;
825 
826 	ENTERFN();
827 
828 	/*
829 	 * RDPTR (1/2 MCLK, 64 PIs)
830 	 * CCPTRREG[31:28] (0x0-0xF)
831 	 * CCPTRREG[27:24] (0x0-0xF)
832 	 */
833 	reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET;
834 	msk = 0xff000000;
835 	temp = ((pi_count / HALF_CLK) << 28) | ((pi_count / HALF_CLK) << 24);
836 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
837 
838 	/* Adjust PI_COUNT */
839 	pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK;
840 
841 	/*
842 	 * PI (1/64 MCLK, 1 PIs)
843 	 * ECCB1DLLPICODER?[29:24] (0x00-0x3F)
844 	 * ECCB1DLLPICODER?[29:24] (0x00-0x3F)
845 	 */
846 	reg = ECCB1DLLPICODER0 + channel * DDRIOCCC_CH_OFFSET;
847 	msk = 0x3f000000;
848 	temp = (pi_count << 24);
849 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
850 
851 	reg = ECCB1DLLPICODER1 + channel * DDRIOCCC_CH_OFFSET;
852 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
853 
854 	reg = ECCB1DLLPICODER2 + channel * DDRIOCCC_CH_OFFSET;
855 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
856 
857 	reg = ECCB1DLLPICODER3 + channel * DDRIOCCC_CH_OFFSET;
858 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
859 
860 	/*
861 	 * DEADBAND
862 	 * CCCFGREG1[13:12] (+1 select)
863 	 * CCCFGREG1[05:04] (enable)
864 	 */
865 	reg = CCCFGREG1 + channel * DDRIOCCC_CH_OFFSET;
866 	msk = 0x00;
867 	temp = 0x00;
868 
869 	/* enable */
870 	msk |= 0x30;
871 	if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
872 		temp |= msk;
873 
874 	/* select */
875 	msk |= 0x3000;
876 	if (pi_count < EARLY_DB)
877 		temp |= msk;
878 
879 	mrc_alt_write_mask(DDRPHY, reg, temp, msk);
880 
881 	/* error check */
882 	if (pi_count > 0x3f)
883 		mrc_post_code(0xee, 0xe6);
884 
885 	LEAVEFN();
886 }
887 
888 /*
889  * This function will return the amount of WCTL delay on the given
890  * channel, rank as an absolute PI count.
891  *
892  * (currently doesn't comprehend rank)
893  */
894 uint32_t get_wctl(uint8_t channel, uint8_t rank)
895 {
896 	uint32_t reg;
897 	uint32_t temp;
898 	uint32_t pi_count;
899 
900 	ENTERFN();
901 
902 	/*
903 	 * RDPTR (1/2 MCLK, 64 PIs)
904 	 * CCPTRREG[31:28] (0x0-0xF)
905 	 * CCPTRREG[27:24] (0x0-0xF)
906 	 */
907 	reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET;
908 	temp = msg_port_alt_read(DDRPHY, reg);
909 	temp >>= 24;
910 	temp &= 0xf;
911 
912 	/* Adjust PI_COUNT */
913 	pi_count = temp * HALF_CLK;
914 
915 	/*
916 	 * PI (1/64 MCLK, 1 PIs)
917 	 * ECCB1DLLPICODER?[29:24] (0x00-0x3F)
918 	 * ECCB1DLLPICODER?[29:24] (0x00-0x3F)
919 	 */
920 	reg = ECCB1DLLPICODER0 + channel * DDRIOCCC_CH_OFFSET;
921 	temp = msg_port_alt_read(DDRPHY, reg);
922 	temp >>= 24;
923 	temp &= 0x3f;
924 
925 	/* Adjust PI_COUNT */
926 	pi_count += temp;
927 
928 	LEAVEFN();
929 
930 	return pi_count;
931 }
932 
933 /*
934  * This function will program the internal Vref setting in a given
935  * byte lane in a given channel.
936  */
937 void set_vref(uint8_t channel, uint8_t byte_lane, uint32_t setting)
938 {
939 	uint32_t reg = (byte_lane & 0x1) ? B1VREFCTL : B0VREFCTL;
940 
941 	ENTERFN();
942 
943 	DPF(D_TRN, "Vref ch%d ln%d : val=%03X\n",
944 	    channel, byte_lane, setting);
945 
946 	mrc_alt_write_mask(DDRPHY, reg + channel * DDRIODQ_CH_OFFSET +
947 		(byte_lane >> 1) * DDRIODQ_BL_OFFSET,
948 		vref_codes[setting] << 2, 0xfc);
949 
950 	/*
951 	 * need to wait ~300ns for Vref to settle
952 	 * (check that this is necessary)
953 	 */
954 	delay_n(300);
955 
956 	/* ??? may need to clear pointers ??? */
957 
958 	LEAVEFN();
959 }
960 
961 /*
962  * This function will return the internal Vref setting for the given
963  * channel, byte_lane.
964  */
965 uint32_t get_vref(uint8_t channel, uint8_t byte_lane)
966 {
967 	uint8_t j;
968 	uint32_t ret_val = sizeof(vref_codes) / 2;
969 	uint32_t reg = (byte_lane & 0x1) ? B1VREFCTL : B0VREFCTL;
970 	uint32_t temp;
971 
972 	ENTERFN();
973 
974 	temp = msg_port_alt_read(DDRPHY, reg + channel * DDRIODQ_CH_OFFSET +
975 		(byte_lane >> 1) * DDRIODQ_BL_OFFSET);
976 	temp >>= 2;
977 	temp &= 0x3f;
978 
979 	for (j = 0; j < sizeof(vref_codes); j++) {
980 		if (vref_codes[j] == temp) {
981 			ret_val = j;
982 			break;
983 		}
984 	}
985 
986 	LEAVEFN();
987 
988 	return ret_val;
989 }
990 
991 /*
992  * This function will return a 32-bit address in the desired
993  * channel and rank.
994  */
995 uint32_t get_addr(uint8_t channel, uint8_t rank)
996 {
997 	uint32_t offset = 32 * 1024 * 1024;	/* 32MB */
998 
999 	/* Begin product specific code */
1000 	if (channel > 0) {
1001 		DPF(D_ERROR, "ILLEGAL CHANNEL\n");
1002 		DEAD_LOOP();
1003 	}
1004 
1005 	if (rank > 1) {
1006 		DPF(D_ERROR, "ILLEGAL RANK\n");
1007 		DEAD_LOOP();
1008 	}
1009 
1010 	/* use 256MB lowest density as per DRP == 0x0003 */
1011 	offset += rank * (256 * 1024 * 1024);
1012 
1013 	return offset;
1014 }
1015 
1016 /*
1017  * This function will sample the DQTRAINSTS registers in the given
1018  * channel/rank SAMPLE_SIZE times looking for a valid '0' or '1'.
1019  *
1020  * It will return an encoded 32-bit date in which each bit corresponds to
1021  * the sampled value on the byte lane.
1022  */
1023 uint32_t sample_dqs(struct mrc_params *mrc_params, uint8_t channel,
1024 		    uint8_t rank, bool rcvn)
1025 {
1026 	uint8_t j;	/* just a counter */
1027 	uint8_t bl;	/* which BL in the module (always 2 per module) */
1028 	uint8_t bl_grp;	/* which BL module */
1029 	/* byte lane divisor */
1030 	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
1031 	uint32_t msk[2];	/* BLx in module */
1032 	/* DQTRAINSTS register contents for each sample */
1033 	uint32_t sampled_val[SAMPLE_SIZE];
1034 	uint32_t num_0s;	/* tracks the number of '0' samples */
1035 	uint32_t num_1s;	/* tracks the number of '1' samples */
1036 	uint32_t ret_val = 0x00;	/* assume all '0' samples */
1037 	uint32_t address = get_addr(channel, rank);
1038 
1039 	/* initialise msk[] */
1040 	msk[0] = rcvn ? (1 << 1) : (1 << 9);	/* BL0 */
1041 	msk[1] = rcvn ? (1 << 0) : (1 << 8);	/* BL1 */
1042 
1043 	/* cycle through each byte lane group */
1044 	for (bl_grp = 0; bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; bl_grp++) {
1045 		/* take SAMPLE_SIZE samples */
1046 		for (j = 0; j < SAMPLE_SIZE; j++) {
1047 			hte_mem_op(address, mrc_params->first_run,
1048 				   rcvn ? 0 : 1);
1049 			mrc_params->first_run = 0;
1050 
1051 			/*
1052 			 * record the contents of the proper
1053 			 * DQTRAINSTS register
1054 			 */
1055 			sampled_val[j] = msg_port_alt_read(DDRPHY,
1056 				DQTRAINSTS +
1057 				bl_grp * DDRIODQ_BL_OFFSET +
1058 				channel * DDRIODQ_CH_OFFSET);
1059 		}
1060 
1061 		/*
1062 		 * look for a majority value (SAMPLE_SIZE / 2) + 1
1063 		 * on the byte lane and set that value in the corresponding
1064 		 * ret_val bit
1065 		 */
1066 		for (bl = 0; bl < 2; bl++) {
1067 			num_0s = 0x00;	/* reset '0' tracker for byte lane */
1068 			num_1s = 0x00;	/* reset '1' tracker for byte lane */
1069 			for (j = 0; j < SAMPLE_SIZE; j++) {
1070 				if (sampled_val[j] & msk[bl])
1071 					num_1s++;
1072 				else
1073 					num_0s++;
1074 			}
1075 		if (num_1s > num_0s)
1076 			ret_val |= (1 << (bl + bl_grp * 2));
1077 		}
1078 	}
1079 
1080 	/*
1081 	 * "ret_val.0" contains the status of BL0
1082 	 * "ret_val.1" contains the status of BL1
1083 	 * "ret_val.2" contains the status of BL2
1084 	 * etc.
1085 	 */
1086 	return ret_val;
1087 }
1088 
1089 /* This function will find the rising edge transition on RCVN or WDQS */
1090 void find_rising_edge(struct mrc_params *mrc_params, uint32_t delay[],
1091 		      uint8_t channel, uint8_t rank, bool rcvn)
1092 {
1093 	bool all_edges_found;	/* determines stop condition */
1094 	bool direction[NUM_BYTE_LANES];	/* direction indicator */
1095 	uint8_t sample;	/* sample counter */
1096 	uint8_t bl;	/* byte lane counter */
1097 	/* byte lane divisor */
1098 	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
1099 	uint32_t sample_result[SAMPLE_CNT];	/* results of sample_dqs() */
1100 	uint32_t temp;
1101 	uint32_t transition_pattern;
1102 
1103 	ENTERFN();
1104 
1105 	/* select hte and request initial configuration */
1106 	select_hte();
1107 	mrc_params->first_run = 1;
1108 
1109 	/* Take 3 sample points (T1,T2,T3) to obtain a transition pattern */
1110 	for (sample = 0; sample < SAMPLE_CNT; sample++) {
1111 		/* program the desired delays for sample */
1112 		for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
1113 			/* increase sample delay by 26 PI (0.2 CLK) */
1114 			if (rcvn) {
1115 				set_rcvn(channel, rank, bl,
1116 					 delay[bl] + sample * SAMPLE_DLY);
1117 			} else {
1118 				set_wdqs(channel, rank, bl,
1119 					 delay[bl] + sample * SAMPLE_DLY);
1120 			}
1121 		}
1122 
1123 		/* take samples (Tsample_i) */
1124 		sample_result[sample] = sample_dqs(mrc_params,
1125 			channel, rank, rcvn);
1126 
1127 		DPF(D_TRN,
1128 		    "Find rising edge %s ch%d rnk%d: #%d dly=%d dqs=%02X\n",
1129 		    rcvn ? "RCVN" : "WDQS", channel, rank, sample,
1130 		    sample * SAMPLE_DLY, sample_result[sample]);
1131 	}
1132 
1133 	/*
1134 	 * This pattern will help determine where we landed and ultimately
1135 	 * how to place RCVEN/WDQS.
1136 	 */
1137 	for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
1138 		/* build transition_pattern (MSB is 1st sample) */
1139 		transition_pattern = 0;
1140 		for (sample = 0; sample < SAMPLE_CNT; sample++) {
1141 			transition_pattern |=
1142 				((sample_result[sample] & (1 << bl)) >> bl) <<
1143 				(SAMPLE_CNT - 1 - sample);
1144 		}
1145 
1146 		DPF(D_TRN, "=== transition pattern %d\n", transition_pattern);
1147 
1148 		/*
1149 		 * set up to look for rising edge based on
1150 		 * transition_pattern
1151 		 */
1152 		switch (transition_pattern) {
1153 		case 0:	/* sampled 0->0->0 */
1154 			/* move forward from T3 looking for 0->1 */
1155 			delay[bl] += 2 * SAMPLE_DLY;
1156 			direction[bl] = FORWARD;
1157 			break;
1158 		case 1:	/* sampled 0->0->1 */
1159 		case 5:	/* sampled 1->0->1 (bad duty cycle) *HSD#237503* */
1160 			/* move forward from T2 looking for 0->1 */
1161 			delay[bl] += 1 * SAMPLE_DLY;
1162 			direction[bl] = FORWARD;
1163 			break;
1164 		case 2:	/* sampled 0->1->0 (bad duty cycle) *HSD#237503* */
1165 		case 3:	/* sampled 0->1->1 */
1166 			/* move forward from T1 looking for 0->1 */
1167 			delay[bl] += 0 * SAMPLE_DLY;
1168 			direction[bl] = FORWARD;
1169 			break;
1170 		case 4:	/* sampled 1->0->0 (assumes BL8, HSD#234975) */
1171 			/* move forward from T3 looking for 0->1 */
1172 			delay[bl] += 2 * SAMPLE_DLY;
1173 			direction[bl] = FORWARD;
1174 			break;
1175 		case 6:	/* sampled 1->1->0 */
1176 		case 7:	/* sampled 1->1->1 */
1177 			/* move backward from T1 looking for 1->0 */
1178 			delay[bl] += 0 * SAMPLE_DLY;
1179 			direction[bl] = BACKWARD;
1180 			break;
1181 		default:
1182 			mrc_post_code(0xee, 0xee);
1183 			break;
1184 		}
1185 
1186 		/* program delays */
1187 		if (rcvn)
1188 			set_rcvn(channel, rank, bl, delay[bl]);
1189 		else
1190 			set_wdqs(channel, rank, bl, delay[bl]);
1191 	}
1192 
1193 	/*
1194 	 * Based on the observed transition pattern on the byte lane,
1195 	 * begin looking for a rising edge with single PI granularity.
1196 	 */
1197 	do {
1198 		all_edges_found = true;	/* assume all byte lanes passed */
1199 		/* take a sample */
1200 		temp = sample_dqs(mrc_params, channel, rank, rcvn);
1201 		/* check all each byte lane for proper edge */
1202 		for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
1203 			if (temp & (1 << bl)) {
1204 				/* sampled "1" */
1205 				if (direction[bl] == BACKWARD) {
1206 					/*
1207 					 * keep looking for edge
1208 					 * on this byte lane
1209 					 */
1210 					all_edges_found = false;
1211 					delay[bl] -= 1;
1212 					if (rcvn) {
1213 						set_rcvn(channel, rank,
1214 							 bl, delay[bl]);
1215 					} else {
1216 						set_wdqs(channel, rank,
1217 							 bl, delay[bl]);
1218 					}
1219 				}
1220 			} else {
1221 				/* sampled "0" */
1222 				if (direction[bl] == FORWARD) {
1223 					/*
1224 					 * keep looking for edge
1225 					 * on this byte lane
1226 					 */
1227 					all_edges_found = false;
1228 					delay[bl] += 1;
1229 					if (rcvn) {
1230 						set_rcvn(channel, rank,
1231 							 bl, delay[bl]);
1232 					} else {
1233 						set_wdqs(channel, rank,
1234 							 bl, delay[bl]);
1235 					}
1236 				}
1237 			}
1238 		}
1239 	} while (!all_edges_found);
1240 
1241 	/* restore DDR idle state */
1242 	dram_init_command(DCMD_PREA(rank));
1243 
1244 	DPF(D_TRN, "Delay %03X %03X %03X %03X\n",
1245 	    delay[0], delay[1], delay[2], delay[3]);
1246 
1247 	LEAVEFN();
1248 }
1249 
1250 /*
1251  * This function will return a 32 bit mask that will be used to
1252  * check for byte lane failures.
1253  */
1254 uint32_t byte_lane_mask(struct mrc_params *mrc_params)
1255 {
1256 	uint32_t j;
1257 	uint32_t ret_val = 0x00;
1258 
1259 	/*
1260 	 * set ret_val based on NUM_BYTE_LANES such that you will check
1261 	 * only BL0 in result
1262 	 *
1263 	 * (each bit in result represents a byte lane)
1264 	 */
1265 	for (j = 0; j < MAX_BYTE_LANES; j += NUM_BYTE_LANES)
1266 		ret_val |= (1 << ((j / NUM_BYTE_LANES) * NUM_BYTE_LANES));
1267 
1268 	/*
1269 	 * HSD#235037
1270 	 * need to adjust the mask for 16-bit mode
1271 	 */
1272 	if (mrc_params->channel_width == X16)
1273 		ret_val |= (ret_val << 2);
1274 
1275 	return ret_val;
1276 }
1277 
1278 /*
1279  * Check memory executing simple write/read/verify at the specified address.
1280  *
1281  * Bits in the result indicate failure on specific byte lane.
1282  */
1283 uint32_t check_rw_coarse(struct mrc_params *mrc_params, uint32_t address)
1284 {
1285 	uint32_t result = 0;
1286 	uint8_t first_run = 0;
1287 
1288 	if (mrc_params->hte_setup) {
1289 		mrc_params->hte_setup = 0;
1290 		first_run = 1;
1291 		select_hte();
1292 	}
1293 
1294 	result = hte_basic_write_read(mrc_params, address, first_run,
1295 				      WRITE_TRAIN);
1296 
1297 	DPF(D_TRN, "check_rw_coarse result is %x\n", result);
1298 
1299 	return result;
1300 }
1301 
1302 /*
1303  * Check memory executing write/read/verify of many data patterns
1304  * at the specified address. Bits in the result indicate failure
1305  * on specific byte lane.
1306  */
1307 uint32_t check_bls_ex(struct mrc_params *mrc_params, uint32_t address)
1308 {
1309 	uint32_t result;
1310 	uint8_t first_run = 0;
1311 
1312 	if (mrc_params->hte_setup) {
1313 		mrc_params->hte_setup = 0;
1314 		first_run = 1;
1315 		select_hte();
1316 	}
1317 
1318 	result = hte_write_stress_bit_lanes(mrc_params, address, first_run);
1319 
1320 	DPF(D_TRN, "check_bls_ex result is %x\n", result);
1321 
1322 	return result;
1323 }
1324 
1325 /*
1326  * 32-bit LFSR with characteristic polynomial: X^32 + X^22 +X^2 + X^1
1327  *
1328  * The function takes pointer to previous 32 bit value and
1329  * modifies it to next value.
1330  */
1331 void lfsr32(uint32_t *lfsr_ptr)
1332 {
1333 	uint32_t bit;
1334 	uint32_t lfsr;
1335 	int i;
1336 
1337 	lfsr = *lfsr_ptr;
1338 
1339 	for (i = 0; i < 32; i++) {
1340 		bit = 1 ^ (lfsr & 1);
1341 		bit = bit ^ ((lfsr & 2) >> 1);
1342 		bit = bit ^ ((lfsr & 4) >> 2);
1343 		bit = bit ^ ((lfsr & 0x400000) >> 22);
1344 
1345 		lfsr = ((lfsr >> 1) | (bit << 31));
1346 	}
1347 
1348 	*lfsr_ptr = lfsr;
1349 }
1350 
1351 /* Clear the pointers in a given byte lane in a given channel */
1352 void clear_pointers(void)
1353 {
1354 	uint8_t channel;
1355 	uint8_t bl;
1356 
1357 	ENTERFN();
1358 
1359 	for (channel = 0; channel < NUM_CHANNELS; channel++) {
1360 		for (bl = 0; bl < NUM_BYTE_LANES; bl++) {
1361 			mrc_alt_write_mask(DDRPHY,
1362 					   B01PTRCTL1 +
1363 					   channel * DDRIODQ_CH_OFFSET +
1364 					   (bl >> 1) * DDRIODQ_BL_OFFSET,
1365 					   ~(1 << 8), (1 << 8));
1366 
1367 			mrc_alt_write_mask(DDRPHY,
1368 					   B01PTRCTL1 +
1369 					   channel * DDRIODQ_CH_OFFSET +
1370 					   (bl >> 1) * DDRIODQ_BL_OFFSET,
1371 					   (1 << 8), (1 << 8));
1372 		}
1373 	}
1374 
1375 	LEAVEFN();
1376 }
1377 
1378 static void print_timings_internal(uint8_t algo, uint8_t channel, uint8_t rank,
1379 				   uint8_t bl_divisor)
1380 {
1381 	uint8_t bl;
1382 
1383 	switch (algo) {
1384 	case RCVN:
1385 		DPF(D_INFO, "\nRCVN[%02d:%02d]", channel, rank);
1386 		break;
1387 	case WDQS:
1388 		DPF(D_INFO, "\nWDQS[%02d:%02d]", channel, rank);
1389 		break;
1390 	case WDQX:
1391 		DPF(D_INFO, "\nWDQx[%02d:%02d]", channel, rank);
1392 		break;
1393 	case RDQS:
1394 		DPF(D_INFO, "\nRDQS[%02d:%02d]", channel, rank);
1395 		break;
1396 	case VREF:
1397 		DPF(D_INFO, "\nVREF[%02d:%02d]", channel, rank);
1398 		break;
1399 	case WCMD:
1400 		DPF(D_INFO, "\nWCMD[%02d:%02d]", channel, rank);
1401 		break;
1402 	case WCTL:
1403 		DPF(D_INFO, "\nWCTL[%02d:%02d]", channel, rank);
1404 		break;
1405 	case WCLK:
1406 		DPF(D_INFO, "\nWCLK[%02d:%02d]", channel, rank);
1407 		break;
1408 	default:
1409 		break;
1410 	}
1411 
1412 	for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
1413 		switch (algo) {
1414 		case RCVN:
1415 			DPF(D_INFO, " %03d", get_rcvn(channel, rank, bl));
1416 			break;
1417 		case WDQS:
1418 			DPF(D_INFO, " %03d", get_wdqs(channel, rank, bl));
1419 			break;
1420 		case WDQX:
1421 			DPF(D_INFO, " %03d", get_wdq(channel, rank, bl));
1422 			break;
1423 		case RDQS:
1424 			DPF(D_INFO, " %03d", get_rdqs(channel, rank, bl));
1425 			break;
1426 		case VREF:
1427 			DPF(D_INFO, " %03d", get_vref(channel, bl));
1428 			break;
1429 		case WCMD:
1430 			DPF(D_INFO, " %03d", get_wcmd(channel));
1431 			break;
1432 		case WCTL:
1433 			DPF(D_INFO, " %03d", get_wctl(channel, rank));
1434 			break;
1435 		case WCLK:
1436 			DPF(D_INFO, " %03d", get_wclk(channel, rank));
1437 			break;
1438 		default:
1439 			break;
1440 		}
1441 	}
1442 }
1443 
1444 void print_timings(struct mrc_params *mrc_params)
1445 {
1446 	uint8_t algo;
1447 	uint8_t channel;
1448 	uint8_t rank;
1449 	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
1450 
1451 	DPF(D_INFO, "\n---------------------------");
1452 	DPF(D_INFO, "\nALGO[CH:RK] BL0 BL1 BL2 BL3");
1453 	DPF(D_INFO, "\n===========================");
1454 
1455 	for (algo = 0; algo < MAX_ALGOS; algo++) {
1456 		for (channel = 0; channel < NUM_CHANNELS; channel++) {
1457 			if (mrc_params->channel_enables & (1 << channel)) {
1458 				for (rank = 0; rank < NUM_RANKS; rank++) {
1459 					if (mrc_params->rank_enables &
1460 						(1 << rank)) {
1461 						print_timings_internal(algo,
1462 							channel, rank,
1463 							bl_divisor);
1464 					}
1465 				}
1466 			}
1467 		}
1468 	}
1469 
1470 	DPF(D_INFO, "\n---------------------------");
1471 	DPF(D_INFO, "\n");
1472 }
1473