]>
git.proxmox.com Git - mirror_edk2.git/blob - QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.c
1 /************************************************************************
3 * Copyright (c) 2013-2015 Intel Corporation.
5 * SPDX-License-Identifier: BSD-2-Clause-Patent
7 ***************************************************************************/
10 #include "memory_options.h"
12 #include "meminit_utils.h"
17 MRCParams_t
*mrc_params
);
19 static uint8_t first_run
= 0;
21 const uint8_t vref_codes
[64] =
22 { // lowest to highest
23 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, // 00 - 15
24 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, // 16 - 31
25 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 32 - 47
26 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F // 48 - 63
30 // Track current post code for debugging purpose
36 // This function will program the RCVEN delays.
37 // (currently doesn't comprehend rank)
49 DPF(D_TRN
, "Rcvn ch%d rnk%d ln%d : pi=%03X\n", channel
, rank
, byte_lane
, pi_count
);
51 // RDPTR (1/2 MCLK, 64 PIs)
52 // BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
53 // BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
54 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
55 msk
= (byte_lane
& BIT0
) ? (BIT23
| BIT22
| BIT21
| BIT20
) : (BIT11
| BIT10
| BIT9
| BIT8
);
56 tempD
= (byte_lane
& BIT0
) ? ((pi_count
/ HALF_CLK
) << 20) : ((pi_count
/ HALF_CLK
) << 8);
57 isbM32m(DDRPHY
, reg
, tempD
, msk
);
60 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
62 // PI (1/64 MCLK, 1 PIs)
63 // BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
64 // BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
65 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
66 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
67 msk
= (BIT29
| BIT28
| BIT27
| BIT26
| BIT25
| BIT24
);
68 tempD
= pi_count
<< 24;
69 isbM32m(DDRPHY
, reg
, tempD
, msk
);
72 // BL0/1 -> B01DBCTL1[08/11] (+1 select)
73 // BL0/1 -> B01DBCTL1[02/05] (enable)
74 reg
= B01DBCTL1
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
78 msk
|= (byte_lane
& BIT0
) ? (BIT5
) : (BIT2
);
79 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
84 msk
|= (byte_lane
& BIT0
) ? (BIT11
) : (BIT8
);
85 if (pi_count
< EARLY_DB
)
89 isbM32m(DDRPHY
, reg
, tempD
, msk
);
94 training_message(channel
, rank
, byte_lane
);
95 post_code(0xEE, 0xE0);
104 // This function will return the current RCVEN delay on the given channel, rank, byte_lane as an absolute PI count.
105 // (currently doesn't comprehend rank)
117 // RDPTR (1/2 MCLK, 64 PIs)
118 // BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
119 // BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
120 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
121 tempD
= isbR32m(DDRPHY
, reg
);
122 tempD
>>= (byte_lane
& BIT0
) ? (20) : (8);
126 pi_count
= tempD
* HALF_CLK
;
128 // PI (1/64 MCLK, 1 PIs)
129 // BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
130 // BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
131 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
132 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
133 tempD
= isbR32m(DDRPHY
, reg
);
146 // This function will program the RDQS delays based on an absolute amount of PIs.
147 // (currently doesn't comprehend rank)
159 DPF(D_TRN
, "Rdqs ch%d rnk%d ln%d : pi=%03X\n", channel
, rank
, byte_lane
, pi_count
);
162 // BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
163 // BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
164 reg
= (byte_lane
& BIT0
) ? (B1RXDQSPICODE
) : (B0RXDQSPICODE
);
165 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
166 msk
= (BIT6
| BIT5
| BIT4
| BIT3
| BIT2
| BIT1
| BIT0
);
167 tempD
= pi_count
<< 0;
168 isbM32m(DDRPHY
, reg
, tempD
, msk
);
170 // error check (shouldn't go above 0x3F)
173 training_message(channel
, rank
, byte_lane
);
174 post_code(0xEE, 0xE1);
183 // This function will return the current RDQS delay on the given channel, rank, byte_lane as an absolute PI count.
184 // (currently doesn't comprehend rank)
197 // BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
198 // BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
199 reg
= (byte_lane
& BIT0
) ? (B1RXDQSPICODE
) : (B0RXDQSPICODE
);
200 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
201 tempD
= isbR32m(DDRPHY
, reg
);
204 pi_count
= tempD
& 0x7F;
212 // This function will program the WDQS delays based on an absolute amount of PIs.
213 // (currently doesn't comprehend rank)
225 DPF(D_TRN
, "Wdqs ch%d rnk%d ln%d : pi=%03X\n", channel
, rank
, byte_lane
, pi_count
);
227 // RDPTR (1/2 MCLK, 64 PIs)
228 // BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
229 // BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
230 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
231 msk
= (byte_lane
& BIT0
) ? (BIT19
| BIT18
| BIT17
| BIT16
) : (BIT7
| BIT6
| BIT5
| BIT4
);
232 tempD
= pi_count
/ HALF_CLK
;
233 tempD
<<= (byte_lane
& BIT0
) ? (16) : (4);
234 isbM32m(DDRPHY
, reg
, tempD
, msk
);
237 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
239 // PI (1/64 MCLK, 1 PIs)
240 // BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
241 // BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
242 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
243 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
244 msk
= (BIT21
| BIT20
| BIT19
| BIT18
| BIT17
| BIT16
);
245 tempD
= pi_count
<< 16;
246 isbM32m(DDRPHY
, reg
, tempD
, msk
);
249 // BL0/1 -> B01DBCTL1[07/10] (+1 select)
250 // BL0/1 -> B01DBCTL1[01/04] (enable)
251 reg
= B01DBCTL1
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
255 msk
|= (byte_lane
& BIT0
) ? (BIT4
) : (BIT1
);
256 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
261 msk
|= (byte_lane
& BIT0
) ? (BIT10
) : (BIT7
);
262 if (pi_count
< EARLY_DB
)
266 isbM32m(DDRPHY
, reg
, tempD
, msk
);
271 training_message(channel
, rank
, byte_lane
);
272 post_code(0xEE, 0xE2);
281 // This function will return the amount of WDQS delay on the given channel, rank, byte_lane as an absolute PI count.
282 // (currently doesn't comprehend rank)
294 // RDPTR (1/2 MCLK, 64 PIs)
295 // BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
296 // BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
297 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
298 tempD
= isbR32m(DDRPHY
, reg
);
299 tempD
>>= (byte_lane
& BIT0
) ? (16) : (4);
303 pi_count
= (tempD
* HALF_CLK
);
305 // PI (1/64 MCLK, 1 PIs)
306 // BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
307 // BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
308 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
309 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
310 tempD
= isbR32m(DDRPHY
, reg
);
323 // This function will program the WDQ delays based on an absolute number of PIs.
324 // (currently doesn't comprehend rank)
336 DPF(D_TRN
, "Wdq ch%d rnk%d ln%d : pi=%03X\n", channel
, rank
, byte_lane
, pi_count
);
338 // RDPTR (1/2 MCLK, 64 PIs)
339 // BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
340 // BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
341 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
342 msk
= (byte_lane
& BIT0
) ? (BIT15
| BIT14
| BIT13
| BIT12
) : (BIT3
| BIT2
| BIT1
| BIT0
);
343 tempD
= pi_count
/ HALF_CLK
;
344 tempD
<<= (byte_lane
& BIT0
) ? (12) : (0);
345 isbM32m(DDRPHY
, reg
, tempD
, msk
);
348 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
350 // PI (1/64 MCLK, 1 PIs)
351 // BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
352 // BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
353 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
354 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
355 msk
= (BIT13
| BIT12
| BIT11
| BIT10
| BIT9
| BIT8
);
356 tempD
= pi_count
<< 8;
357 isbM32m(DDRPHY
, reg
, tempD
, msk
);
360 // BL0/1 -> B01DBCTL1[06/09] (+1 select)
361 // BL0/1 -> B01DBCTL1[00/03] (enable)
362 reg
= B01DBCTL1
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
366 msk
|= (byte_lane
& BIT0
) ? (BIT3
) : (BIT0
);
367 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
372 msk
|= (byte_lane
& BIT0
) ? (BIT9
) : (BIT6
);
373 if (pi_count
< EARLY_DB
)
377 isbM32m(DDRPHY
, reg
, tempD
, msk
);
382 training_message(channel
, rank
, byte_lane
);
383 post_code(0xEE, 0xE3);
392 // This function will return the amount of WDQ delay on the given channel, rank, byte_lane as an absolute PI count.
393 // (currently doesn't comprehend rank)
405 // RDPTR (1/2 MCLK, 64 PIs)
406 // BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
407 // BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
408 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
409 tempD
= isbR32m(DDRPHY
, reg
);
410 tempD
>>= (byte_lane
& BIT0
) ? (12) : (0);
414 pi_count
= (tempD
* HALF_CLK
);
416 // PI (1/64 MCLK, 1 PIs)
417 // BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
418 // BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
419 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
420 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
421 tempD
= isbR32m(DDRPHY
, reg
);
434 // This function will program the WCMD delays based on an absolute number of PIs.
444 // RDPTR (1/2 MCLK, 64 PIs)
445 // CMDPTRREG[11:08] (0x0-0xF)
446 reg
= CMDPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
447 msk
= (BIT11
| BIT10
| BIT9
| BIT8
);
448 tempD
= pi_count
/ HALF_CLK
;
450 isbM32m(DDRPHY
, reg
, tempD
, msk
);
453 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
455 // PI (1/64 MCLK, 1 PIs)
456 // CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
457 // CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
458 // CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
459 // CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
460 // CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
461 // CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
462 // CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
463 // CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
464 reg
= CMDDLLPICODER1
+ (channel
* DDRIOCCC_CH_OFFSET
);
466 msk
= (BIT29
| BIT28
| BIT27
| BIT26
| BIT25
| BIT24
) | (BIT21
| BIT20
| BIT19
| BIT18
| BIT17
| BIT16
)
467 | (BIT13
| BIT12
| BIT11
| BIT10
| BIT9
| BIT8
) | (BIT5
| BIT4
| BIT3
| BIT2
| BIT1
| BIT0
);
469 tempD
= (pi_count
<< 24) | (pi_count
<< 16) | (pi_count
<< 8) | (pi_count
<< 0);
471 isbM32m(DDRPHY
, reg
, tempD
, msk
);
472 reg
= CMDDLLPICODER0
+ (channel
* DDRIOCCC_CH_OFFSET
); // PO
473 isbM32m(DDRPHY
, reg
, tempD
, msk
);
476 // CMDCFGREG0[17] (+1 select)
477 // CMDCFGREG0[16] (enable)
478 reg
= CMDCFGREG0
+ (channel
* DDRIOCCC_CH_OFFSET
);
483 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
489 if (pi_count
< EARLY_DB
)
493 isbM32m(DDRPHY
, reg
, tempD
, msk
);
498 post_code(0xEE, 0xE4);
507 // This function will return the amount of WCMD delay on the given channel as an absolute PI count.
516 // RDPTR (1/2 MCLK, 64 PIs)
517 // CMDPTRREG[11:08] (0x0-0xF)
518 reg
= CMDPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
519 tempD
= isbR32m(DDRPHY
, reg
);
524 pi_count
= tempD
* HALF_CLK
;
526 // PI (1/64 MCLK, 1 PIs)
527 // CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
528 // CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
529 // CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
530 // CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
531 // CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
532 // CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
533 // CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
534 // CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
535 reg
= CMDDLLPICODER1
+ (channel
* DDRIOCCC_CH_OFFSET
);
536 tempD
= isbR32m(DDRPHY
, reg
);
549 // This function will program the WCLK delays based on an absolute number of PIs.
560 // RDPTR (1/2 MCLK, 64 PIs)
561 // CCPTRREG[15:12] -> CLK1 (0x0-0xF)
562 // CCPTRREG[11:08] -> CLK0 (0x0-0xF)
563 reg
= CCPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
564 msk
= (BIT15
| BIT14
| BIT13
| BIT12
) | (BIT11
| BIT10
| BIT9
| BIT8
);
565 tempD
= ((pi_count
/ HALF_CLK
) << 12) | ((pi_count
/ HALF_CLK
) << 8);
566 isbM32m(DDRPHY
, reg
, tempD
, msk
);
569 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
571 // PI (1/64 MCLK, 1 PIs)
572 // ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
573 // ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
574 reg
= (rank
) ? (ECCB1DLLPICODER0
) : (ECCB1DLLPICODER0
);
575 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
576 msk
= (BIT21
| BIT20
| BIT19
| BIT18
| BIT17
| BIT16
) | (BIT13
| BIT12
| BIT11
| BIT10
| BIT9
| BIT8
);
577 tempD
= (pi_count
<< 16) | (pi_count
<< 8);
578 isbM32m(DDRPHY
, reg
, tempD
, msk
);
579 reg
= (rank
) ? (ECCB1DLLPICODER1
) : (ECCB1DLLPICODER1
);
580 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
581 isbM32m(DDRPHY
, reg
, tempD
, msk
);
582 reg
= (rank
) ? (ECCB1DLLPICODER2
) : (ECCB1DLLPICODER2
);
583 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
584 isbM32m(DDRPHY
, reg
, tempD
, msk
);
585 reg
= (rank
) ? (ECCB1DLLPICODER3
) : (ECCB1DLLPICODER3
);
586 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
587 isbM32m(DDRPHY
, reg
, tempD
, msk
);
590 // CCCFGREG1[11:08] (+1 select)
591 // CCCFGREG1[03:00] (enable)
592 reg
= CCCFGREG1
+ (channel
* DDRIOCCC_CH_OFFSET
);
596 msk
|= (BIT3
| BIT2
| BIT1
| BIT0
); // only ??? matters
597 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
602 msk
|= (BIT11
| BIT10
| BIT9
| BIT8
); // only ??? matters
603 if (pi_count
< EARLY_DB
)
607 isbM32m(DDRPHY
, reg
, tempD
, msk
);
612 post_code(0xEE, 0xE5);
621 // This function will return the amout of WCLK delay on the given channel, rank as an absolute PI count.
631 // RDPTR (1/2 MCLK, 64 PIs)
632 // CCPTRREG[15:12] -> CLK1 (0x0-0xF)
633 // CCPTRREG[11:08] -> CLK0 (0x0-0xF)
634 reg
= CCPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
635 tempD
= isbR32m(DDRPHY
, reg
);
636 tempD
>>= (rank
) ? (12) : (8);
640 pi_count
= tempD
* HALF_CLK
;
642 // PI (1/64 MCLK, 1 PIs)
643 // ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
644 // ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
645 reg
= (rank
) ? (ECCB1DLLPICODER0
) : (ECCB1DLLPICODER0
);
646 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
647 tempD
= isbR32m(DDRPHY
, reg
);
648 tempD
>>= (rank
) ? (16) : (8);
659 // This function will program the WCTL delays based on an absolute number of PIs.
660 // (currently doesn't comprehend rank)
672 // RDPTR (1/2 MCLK, 64 PIs)
673 // CCPTRREG[31:28] (0x0-0xF)
674 // CCPTRREG[27:24] (0x0-0xF)
675 reg
= CCPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
676 msk
= (BIT31
| BIT30
| BIT29
| BIT28
) | (BIT27
| BIT26
| BIT25
| BIT24
);
677 tempD
= ((pi_count
/ HALF_CLK
) << 28) | ((pi_count
/ HALF_CLK
) << 24);
678 isbM32m(DDRPHY
, reg
, tempD
, msk
);
681 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
683 // PI (1/64 MCLK, 1 PIs)
684 // ECCB1DLLPICODER?[29:24] (0x00-0x3F)
685 // ECCB1DLLPICODER?[29:24] (0x00-0x3F)
686 reg
= ECCB1DLLPICODER0
+ (channel
* DDRIOCCC_CH_OFFSET
);
687 msk
= (BIT29
| BIT28
| BIT27
| BIT26
| BIT25
| BIT24
);
688 tempD
= (pi_count
<< 24);
689 isbM32m(DDRPHY
, reg
, tempD
, msk
);
690 reg
= ECCB1DLLPICODER1
+ (channel
* DDRIOCCC_CH_OFFSET
);
691 isbM32m(DDRPHY
, reg
, tempD
, msk
);
692 reg
= ECCB1DLLPICODER2
+ (channel
* DDRIOCCC_CH_OFFSET
);
693 isbM32m(DDRPHY
, reg
, tempD
, msk
);
694 reg
= ECCB1DLLPICODER3
+ (channel
* DDRIOCCC_CH_OFFSET
);
695 isbM32m(DDRPHY
, reg
, tempD
, msk
);
698 // CCCFGREG1[13:12] (+1 select)
699 // CCCFGREG1[05:04] (enable)
700 reg
= CCCFGREG1
+ (channel
* DDRIOCCC_CH_OFFSET
);
704 msk
|= (BIT5
| BIT4
); // only ??? matters
705 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
710 msk
|= (BIT13
| BIT12
); // only ??? matters
711 if (pi_count
< EARLY_DB
)
715 isbM32m(DDRPHY
, reg
, tempD
, msk
);
720 post_code(0xEE, 0xE6);
729 // This function will return the amount of WCTL delay on the given channel, rank as an absolute PI count.
730 // (currently doesn't comprehend rank)
741 // RDPTR (1/2 MCLK, 64 PIs)
742 // CCPTRREG[31:28] (0x0-0xF)
743 // CCPTRREG[27:24] (0x0-0xF)
744 reg
= CCPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
745 tempD
= isbR32m(DDRPHY
, reg
);
750 pi_count
= tempD
* HALF_CLK
;
752 // PI (1/64 MCLK, 1 PIs)
753 // ECCB1DLLPICODER?[29:24] (0x00-0x3F)
754 // ECCB1DLLPICODER?[29:24] (0x00-0x3F)
755 reg
= ECCB1DLLPICODER0
+ (channel
* DDRIOCCC_CH_OFFSET
);
756 tempD
= isbR32m(DDRPHY
, reg
);
769 // This function will program the internal Vref setting in a given byte lane in a given channel.
775 uint32_t reg
= (byte_lane
& 0x1) ? (B1VREFCTL
) : (B0VREFCTL
);
778 DPF(D_TRN
, "Vref ch%d ln%d : val=%03X\n", channel
, byte_lane
, setting
);
780 isbM32m(DDRPHY
, (reg
+ (channel
* DDRIODQ_CH_OFFSET
) + ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
)),
781 (vref_codes
[setting
] << 2), (BIT7
| BIT6
| BIT5
| BIT4
| BIT3
| BIT2
));
782 //isbM32m(DDRPHY, (reg + (channel * DDRIODQ_CH_OFFSET) + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET)), (setting<<2), (BIT7|BIT6|BIT5|BIT4|BIT3|BIT2));
783 // need to wait ~300ns for Vref to settle (check that this is necessary)
785 // ??? may need to clear pointers ???
792 // This function will return the internal Vref setting for the given channel, byte_lane;
798 uint32_t ret_val
= sizeof(vref_codes
) / 2;
799 uint32_t reg
= (byte_lane
& 0x1) ? (B1VREFCTL
) : (B0VREFCTL
);
804 tempD
= isbR32m(DDRPHY
, (reg
+ (channel
* DDRIODQ_CH_OFFSET
) + ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
)));
807 for (j
= 0; j
< sizeof(vref_codes
); j
++)
809 if (vref_codes
[j
] == tempD
)
821 // This function will be used to clear the pointers in a given byte lane in a given channel.
829 for (channel_i
= 0; channel_i
< NUM_CHANNELS
; channel_i
++)
831 for (bl_i
= 0; bl_i
< NUM_BYTE_LANES
; bl_i
++)
833 isbM32m(DDRPHY
, (B01PTRCTL1
+ (channel_i
* DDRIODQ_CH_OFFSET
) + ((bl_i
>> 1) * DDRIODQ_BL_OFFSET
)), ~(BIT8
),
835 //delay_m(1); // DEBUG
836 isbM32m(DDRPHY
, (B01PTRCTL1
+ (channel_i
* DDRIODQ_CH_OFFSET
) + ((bl_i
>> 1) * DDRIODQ_BL_OFFSET
)), (BIT8
),
844 // void enable_cache:
848 // Cache control not used in Quark MRC
852 // void disable_cache:
856 // Cache control not used in Quark MRC
860 // Send DRAM command, data should be formated
861 // using DCMD_Xxxx macro or emrsXCommand structure.
862 static void dram_init_command(
870 // This function will find the rising edge transition on RCVN or WDQS.
871 void find_rising_edge(
872 MRCParams_t
*mrc_params
,
879 #define SAMPLE_CNT 3 // number of sample points
880 #define SAMPLE_DLY 26 // number of PIs to increment per sample
881 #define FORWARD true // indicates to increase delays when looking for edge
882 #define BACKWARD false // indicates to decrease delays when looking for edge
884 bool all_edges_found
; // determines stop condition
885 bool direction
[NUM_BYTE_LANES
]; // direction indicator
886 uint8_t sample_i
; // sample counter
887 uint8_t bl_i
; // byte lane counter
888 uint8_t bl_divisor
= (mrc_params
->channel_width
== x16
) ? 2 : 1; // byte lane divisor
889 uint32_t sample_result
[SAMPLE_CNT
]; // results of "sample_dqs()"
890 uint32_t tempD
; // temporary DWORD
891 uint32_t transition_pattern
;
895 // select hte and request initial configuration
896 select_hte(mrc_params
);
899 // Take 3 sample points (T1,T2,T3) to obtain a transition pattern.
900 for (sample_i
= 0; sample_i
< SAMPLE_CNT
; sample_i
++)
902 // program the desired delays for sample
903 for (bl_i
= 0; bl_i
< (NUM_BYTE_LANES
/ bl_divisor
); bl_i
++)
905 // increase sample delay by 26 PI (0.2 CLK)
908 set_rcvn(channel
, rank
, bl_i
, delay
[bl_i
] + (sample_i
* SAMPLE_DLY
));
912 set_wdqs(channel
, rank
, bl_i
, delay
[bl_i
] + (sample_i
* SAMPLE_DLY
));
915 // take samples (Tsample_i)
916 sample_result
[sample_i
] = sample_dqs(mrc_params
, channel
, rank
, rcvn
);
918 DPF(D_TRN
, "Find rising edge %s ch%d rnk%d: #%d dly=%d dqs=%02X\n",
919 (rcvn
? "RCVN" : "WDQS"), channel
, rank
,
920 sample_i
, sample_i
* SAMPLE_DLY
, sample_result
[sample_i
]);
924 // This pattern will help determine where we landed and ultimately how to place RCVEN/WDQS.
925 for (bl_i
= 0; bl_i
< (NUM_BYTE_LANES
/ bl_divisor
); bl_i
++)
927 // build "transition_pattern" (MSB is 1st sample)
928 transition_pattern
= 0x00;
929 for (sample_i
= 0; sample_i
< SAMPLE_CNT
; sample_i
++)
931 transition_pattern
|= ((sample_result
[sample_i
] & (1 << bl_i
)) >> bl_i
) << (SAMPLE_CNT
- 1 - sample_i
);
934 DPF(D_TRN
, "=== transition pattern %d\n", transition_pattern
);
936 // set up to look for rising edge based on "transition_pattern"
937 switch (transition_pattern
)
939 case 0x00: // sampled 0->0->0
940 // move forward from T3 looking for 0->1
941 delay
[bl_i
] += 2 * SAMPLE_DLY
;
942 direction
[bl_i
] = FORWARD
;
944 case 0x01: // sampled 0->0->1
945 case 0x05: // sampled 1->0->1 (bad duty cycle) *HSD#237503*
946 // move forward from T2 looking for 0->1
947 delay
[bl_i
] += 1 * SAMPLE_DLY
;
948 direction
[bl_i
] = FORWARD
;
951 // case 0x02: // sampled 0->1->0 (bad duty cycle)
952 // training_message(channel, rank, bl_i);
953 // post_code(0xEE, 0xE8);
955 case 0x02: // sampled 0->1->0 (bad duty cycle) *HSD#237503*
956 case 0x03: // sampled 0->1->1
957 // move forward from T1 looking for 0->1
958 delay
[bl_i
] += 0 * SAMPLE_DLY
;
959 direction
[bl_i
] = FORWARD
;
961 case 0x04: // sampled 1->0->0 (assumes BL8, HSD#234975)
962 // move forward from T3 looking for 0->1
963 delay
[bl_i
] += 2 * SAMPLE_DLY
;
964 direction
[bl_i
] = FORWARD
;
967 // case 0x05: // sampled 1->0->1 (bad duty cycle)
968 // training_message(channel, rank, bl_i);
969 // post_code(0xEE, 0xE9);
971 case 0x06: // sampled 1->1->0
972 case 0x07: // sampled 1->1->1
973 // move backward from T1 looking for 1->0
974 delay
[bl_i
] += 0 * SAMPLE_DLY
;
975 direction
[bl_i
] = BACKWARD
;
978 post_code(0xEE, 0xEE);
980 } // transition_pattern switch
984 set_rcvn(channel
, rank
, bl_i
, delay
[bl_i
]);
988 set_wdqs(channel
, rank
, bl_i
, delay
[bl_i
]);
992 // Based on the observed transition pattern on the byte lane,
993 // begin looking for a rising edge with single PI granularity.
996 all_edges_found
= true; // assume all byte lanes passed
997 tempD
= sample_dqs(mrc_params
, channel
, rank
, rcvn
); // take a sample
998 // check all each byte lane for proper edge
999 for (bl_i
= 0; bl_i
< (NUM_BYTE_LANES
/ bl_divisor
); bl_i
++)
1001 if (tempD
& (1 << bl_i
))
1004 if (direction
[bl_i
] == BACKWARD
)
1006 // keep looking for edge on this byte lane
1007 all_edges_found
= false;
1011 set_rcvn(channel
, rank
, bl_i
, delay
[bl_i
]);
1015 set_wdqs(channel
, rank
, bl_i
, delay
[bl_i
]);
1022 if (direction
[bl_i
] == FORWARD
)
1024 // keep looking for edge on this byte lane
1025 all_edges_found
= false;
1029 set_rcvn(channel
, rank
, bl_i
, delay
[bl_i
]);
1033 set_wdqs(channel
, rank
, bl_i
, delay
[bl_i
]);
1038 } while (!all_edges_found
);
1040 // restore DDR idle state
1041 dram_init_command(DCMD_PREA(rank
));
1043 DPF(D_TRN
, "Delay %03X %03X %03X %03X\n",
1044 delay
[0], delay
[1], delay
[2], delay
[3]);
1052 // This function will sample the DQTRAINSTS registers in the given channel/rank SAMPLE_SIZE times looking for a valid '0' or '1'.
1053 // It will return an encoded DWORD in which each bit corresponds to the sampled value on the byte lane.
1054 uint32_t sample_dqs(
1055 MRCParams_t
*mrc_params
,
1060 uint8_t j
; // just a counter
1061 uint8_t bl_i
; // which BL in the module (always 2 per module)
1062 uint8_t bl_grp
; // which BL module
1063 uint8_t bl_divisor
= (mrc_params
->channel_width
== x16
) ? 2 : 1; // byte lane divisor
1064 uint32_t msk
[2]; // BLx in module
1065 uint32_t sampled_val
[SAMPLE_SIZE
]; // DQTRAINSTS register contents for each sample
1066 uint32_t num_0s
; // tracks the number of '0' samples
1067 uint32_t num_1s
; // tracks the number of '1' samples
1068 uint32_t ret_val
= 0x00; // assume all '0' samples
1069 uint32_t address
= get_addr(mrc_params
, channel
, rank
);
1071 // initialise "msk[]"
1072 msk
[0] = (rcvn
) ? (BIT1
) : (BIT9
); // BL0
1073 msk
[1] = (rcvn
) ? (BIT0
) : (BIT8
); // BL1
1076 // cycle through each byte lane group
1077 for (bl_grp
= 0; bl_grp
< (NUM_BYTE_LANES
/ bl_divisor
) / 2; bl_grp
++)
1079 // take SAMPLE_SIZE samples
1080 for (j
= 0; j
< SAMPLE_SIZE
; j
++)
1082 HteMemOp(address
, first_run
, rcvn
?0:1);
1085 // record the contents of the proper DQTRAINSTS register
1086 sampled_val
[j
] = isbR32m(DDRPHY
, (DQTRAINSTS
+ (bl_grp
* DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
)));
1088 // look for a majority value ( (SAMPLE_SIZE/2)+1 ) on the byte lane
1089 // and set that value in the corresponding "ret_val" bit
1090 for (bl_i
= 0; bl_i
< 2; bl_i
++)
1092 num_0s
= 0x00; // reset '0' tracker for byte lane
1093 num_1s
= 0x00; // reset '1' tracker for byte lane
1094 for (j
= 0; j
< SAMPLE_SIZE
; j
++)
1096 if (sampled_val
[j
] & msk
[bl_i
])
1105 if (num_1s
> num_0s
)
1107 ret_val
|= (1 << (bl_i
+ (bl_grp
* 2)));
1112 // "ret_val.0" contains the status of BL0
1113 // "ret_val.1" contains the status of BL1
1114 // "ret_val.2" contains the status of BL2
1121 // This function will return a 32 bit address in the desired channel and rank.
1123 MRCParams_t
*mrc_params
,
1127 uint32_t offset
= 0x02000000; // 32MB
1129 // Begin product specific code
1132 DPF(D_ERROR
, "ILLEGAL CHANNEL\n");
1138 DPF(D_ERROR
, "ILLEGAL RANK\n");
1142 // use 256MB lowest density as per DRP == 0x0003
1143 offset
+= rank
* (256 * 1024 * 1024);
1150 // This function will return a 32 bit mask that will be used to check for byte lane failures.
1151 uint32_t byte_lane_mask(
1152 MRCParams_t
*mrc_params
)
1155 uint32_t ret_val
= 0x00;
1157 // set "ret_val" based on NUM_BYTE_LANES such that you will check only BL0 in "result"
1158 // (each bit in "result" represents a byte lane)
1159 for (j
= 0; j
< MAX_BYTE_LANES
; j
+= NUM_BYTE_LANES
)
1161 ret_val
|= (1 << ((j
/ NUM_BYTE_LANES
) * NUM_BYTE_LANES
));
1165 // need to adjust the mask for 16-bit mode
1166 if (mrc_params
->channel_width
== x16
)
1168 ret_val
|= (ret_val
<< 2);
1177 // This function will do some assembly to return TSC register contents as a uint64_t.
1181 volatile uint64_t tsc
; // EDX:EAX
1183 #if defined (SIM) || defined (GCC)
1184 volatile uint32_t tscH
; // EDX
1185 volatile uint32_t tscL
;// EAX
1187 asm("rdtsc":"=a"(tscL
),"=d"(tscH
));
1189 tsc
= (tsc
<<32)|tscL
;
1199 // This function returns the TSC frequency in MHz
1200 uint32_t get_tsc_freq(
1203 static uint32_t freq
[] =
1204 { 533, 400, 200, 100 };
1207 fuse
= (isbR32m(FUSE
, 0) >> 12) & (BIT1
|BIT0
);
1209 // todo!!! Fixed 533MHz for emulation or debugging
1218 // This is a simple delay function.
1219 // It takes "nanoseconds" as a parameter.
1221 uint32_t nanoseconds
)
1223 // 1000 MHz clock has 1ns period --> no conversion required
1224 uint64_t final_tsc
= read_tsc();
1225 final_tsc
+= ((get_tsc_freq() * (nanoseconds
)) / 1000);
1227 while (read_tsc() < final_tsc
)
1235 // This is a simple delay function.
1236 // It takes "microseconds as a parameter.
1238 uint32_t microseconds
)
1240 // 64 bit math is not an option, just use loops
1241 while (microseconds
--)
1250 // This is a simple delay function.
1251 // It takes "milliseconds" as a parameter.
1253 uint32_t milliseconds
)
1255 // 64 bit math is not an option, just use loops
1256 while (milliseconds
--)
1265 // This is a simple delay function.
1266 // It takes "seconds" as a parameter.
1270 // 64 bit math is not an option, just use loops
1280 // This function will output the POST CODE to the four 7-Segment LED displays.
1286 // Update global variable for execution tracking in debug env
1287 PostCode
= ((major
<< 8) | minor
);
1290 // send message to UART
1291 DPF(D_INFO
, "POST: 0x%01X%02X\n", major
, minor
);
1296 // todo!!! Consider updating error status and exit MRC
1298 // enable Ctrl-C handling
1299 for(;;) delay_n(100);
1306 void training_message(
1311 // send message to UART
1312 DPF(D_INFO
, "CH%01X RK%01X BL%01X\n", channel
, rank
, byte_lane
);
1317 MRCParams_t
*mrc_params
)
1323 uint8_t bl_divisor
= (mrc_params
->channel_width
== x16
) ? 2 : 1;
1325 DPF(D_INFO
, "\n---------------------------");
1326 DPF(D_INFO
, "\nALGO[CH:RK] BL0 BL1 BL2 BL3");
1327 DPF(D_INFO
, "\n===========================");
1328 for (algo_i
= 0; algo_i
< eMAX_ALGOS
; algo_i
++)
1330 for (channel_i
= 0; channel_i
< NUM_CHANNELS
; channel_i
++)
1332 if (mrc_params
->channel_enables
& (1 << channel_i
))
1334 for (rank_i
= 0; rank_i
< NUM_RANKS
; rank_i
++)
1336 if (mrc_params
->rank_enables
& (1 << rank_i
))
1341 DPF(D_INFO
, "\nRCVN[%02d:%02d]", channel_i
, rank_i
);
1344 DPF(D_INFO
, "\nWDQS[%02d:%02d]", channel_i
, rank_i
);
1347 DPF(D_INFO
, "\nWDQx[%02d:%02d]", channel_i
, rank_i
);
1350 DPF(D_INFO
, "\nRDQS[%02d:%02d]", channel_i
, rank_i
);
1353 DPF(D_INFO
, "\nVREF[%02d:%02d]", channel_i
, rank_i
);
1356 DPF(D_INFO
, "\nWCMD[%02d:%02d]", channel_i
, rank_i
);
1359 DPF(D_INFO
, "\nWCTL[%02d:%02d]", channel_i
, rank_i
);
1362 DPF(D_INFO
, "\nWCLK[%02d:%02d]", channel_i
, rank_i
);
1367 for (bl_i
= 0; bl_i
< (NUM_BYTE_LANES
/ bl_divisor
); bl_i
++)
1372 DPF(D_INFO
, " %03d", get_rcvn(channel_i
, rank_i
, bl_i
));
1375 DPF(D_INFO
, " %03d", get_wdqs(channel_i
, rank_i
, bl_i
));
1378 DPF(D_INFO
, " %03d", get_wdq(channel_i
, rank_i
, bl_i
));
1381 DPF(D_INFO
, " %03d", get_rdqs(channel_i
, rank_i
, bl_i
));
1384 DPF(D_INFO
, " %03d", get_vref(channel_i
, bl_i
));
1387 DPF(D_INFO
, " %03d", get_wcmd(channel_i
));
1390 DPF(D_INFO
, " %03d", get_wctl(channel_i
, rank_i
));
1393 DPF(D_INFO
, " %03d", get_wclk(channel_i
, rank_i
));
1399 } // if rank_i enabled
1401 } // if channel_i enabled
1404 DPF(D_INFO
, "\n---------------------------");
1409 // 32 bit LFSR with characteristic polynomial: X^32 + X^22 +X^2 + X^1
1410 // The function takes pointer to previous 32 bit value and modifies it to next value.
1420 for (i
= 0; i
< 32; i
++)
1422 bit
= 1 ^ (lfsr
& BIT0
);
1423 bit
= bit
^ ((lfsr
& BIT1
) >> 1);
1424 bit
= bit
^ ((lfsr
& BIT2
) >> 2);
1425 bit
= bit
^ ((lfsr
& BIT22
) >> 22);
1427 lfsr
= ((lfsr
>> 1) | (bit
<< 31));
1434 // The purpose of this function is to ensure the SEC comes out of reset
1435 // and IA initiates the SEC enabling Memory Scrambling.
1436 void enable_scrambling(
1437 MRCParams_t
*mrc_params
)
1442 if (mrc_params
->scrambling_enables
== 0)
1447 // 32 bit seed is always stored in BIOS NVM.
1448 lfsr
= mrc_params
->timings
.scrambler_seed
;
1450 if (mrc_params
->boot_mode
== bmCold
)
1452 // factory value is 0 and in first boot, a clock based seed is loaded.
1455 lfsr
= read_tsc() & 0x0FFFFFFF; // get seed from system clock and make sure it is not all 1's
1457 // need to replace scrambler
1458 // get next 32bit LFSR 16 times which is the last part of the previous scrambler vector.
1461 for (i
= 0; i
< 16; i
++)
1466 mrc_params
->timings
.scrambler_seed
= lfsr
; // save new seed.
1469 // In warm boot or S3 exit, we have the previous seed.
1470 // In cold boot, we have the last 32bit LFSR which is the new seed.
1471 lfsr32(&lfsr
); // shift to next value
1472 isbW32m(MCU
, SCRMSEED
, (lfsr
& 0x0003FFFF));
1473 for (i
= 0; i
< 2; i
++)
1475 isbW32m(MCU
, SCRMLO
+ i
, (lfsr
& 0xAAAAAAAA));
1482 // This function will store relevant timing data
1483 // This data will be used on subsequent boots to speed up boot times
1484 // and is required for Suspend To RAM capabilities.
1486 MRCParams_t
*mrc_params
)
1489 MrcTimings_t
*mt
= &mrc_params
->timings
;
1491 for (ch
= 0; ch
< NUM_CHANNELS
; ch
++)
1493 for (rk
= 0; rk
< NUM_RANKS
; rk
++)
1495 for (bl
= 0; bl
< NUM_BYTE_LANES
; bl
++)
1497 mt
->rcvn
[ch
][rk
][bl
] = get_rcvn(ch
, rk
, bl
); // RCVN
1498 mt
->rdqs
[ch
][rk
][bl
] = get_rdqs(ch
, rk
, bl
); // RDQS
1499 mt
->wdqs
[ch
][rk
][bl
] = get_wdqs(ch
, rk
, bl
); // WDQS
1500 mt
->wdq
[ch
][rk
][bl
] = get_wdq(ch
, rk
, bl
); // WDQ
1503 mt
->vref
[ch
][bl
] = get_vref(ch
, bl
); // VREF (RANK0 only)
1506 mt
->wctl
[ch
][rk
] = get_wctl(ch
, rk
); // WCTL
1508 mt
->wcmd
[ch
] = get_wcmd(ch
); // WCMD
1511 // need to save for a case of changing frequency after warm reset
1512 mt
->ddr_speed
= mrc_params
->ddr_speed
;
1517 // This function will retrieve relevant timing data
1518 // This data will be used on subsequent boots to speed up boot times
1519 // and is required for Suspend To RAM capabilities.
1520 void restore_timings(
1521 MRCParams_t
*mrc_params
)
1524 const MrcTimings_t
*mt
= &mrc_params
->timings
;
1526 for (ch
= 0; ch
< NUM_CHANNELS
; ch
++)
1528 for (rk
= 0; rk
< NUM_RANKS
; rk
++)
1530 for (bl
= 0; bl
< NUM_BYTE_LANES
; bl
++)
1532 set_rcvn(ch
, rk
, bl
, mt
->rcvn
[ch
][rk
][bl
]); // RCVN
1533 set_rdqs(ch
, rk
, bl
, mt
->rdqs
[ch
][rk
][bl
]); // RDQS
1534 set_wdqs(ch
, rk
, bl
, mt
->wdqs
[ch
][rk
][bl
]); // WDQS
1535 set_wdq(ch
, rk
, bl
, mt
->wdq
[ch
][rk
][bl
]); // WDQ
1538 set_vref(ch
, bl
, mt
->vref
[ch
][bl
]); // VREF (RANK0 only)
1541 set_wctl(ch
, rk
, mt
->wctl
[ch
][rk
]); // WCTL
1543 set_wcmd(ch
, mt
->wcmd
[ch
]); // WCMD
1549 // Configure default settings normally set as part of read training
1550 // Some defaults have to be set earlier as they may affect earlier
1552 void default_timings(
1553 MRCParams_t
*mrc_params
)
1557 for (ch
= 0; ch
< NUM_CHANNELS
; ch
++)
1559 for (rk
= 0; rk
< NUM_RANKS
; rk
++)
1561 for (bl
= 0; bl
< NUM_BYTE_LANES
; bl
++)
1563 set_rdqs(ch
, rk
, bl
, 24); // RDQS
1566 set_vref(ch
, bl
, 32); // VREF (RANK0 only)