]>
git.proxmox.com Git - mirror_edk2.git/blob - QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.c
1 /************************************************************************
3 * Copyright (c) 2013-2015 Intel Corporation.
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 ***************************************************************************/
16 #include "memory_options.h"
18 #include "meminit_utils.h"
23 MRCParams_t
*mrc_params
);
25 static uint8_t first_run
= 0;
27 const uint8_t vref_codes
[64] =
28 { // lowest to highest
29 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, // 00 - 15
30 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, // 16 - 31
31 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 32 - 47
32 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F // 48 - 63
36 // Track current post code for debugging purpose
42 // This function will program the RCVEN delays.
43 // (currently doesn't comprehend rank)
55 DPF(D_TRN
, "Rcvn ch%d rnk%d ln%d : pi=%03X\n", channel
, rank
, byte_lane
, pi_count
);
57 // RDPTR (1/2 MCLK, 64 PIs)
58 // BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
59 // BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
60 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
61 msk
= (byte_lane
& BIT0
) ? (BIT23
| BIT22
| BIT21
| BIT20
) : (BIT11
| BIT10
| BIT9
| BIT8
);
62 tempD
= (byte_lane
& BIT0
) ? ((pi_count
/ HALF_CLK
) << 20) : ((pi_count
/ HALF_CLK
) << 8);
63 isbM32m(DDRPHY
, reg
, tempD
, msk
);
66 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
68 // PI (1/64 MCLK, 1 PIs)
69 // BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
70 // BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
71 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
72 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
73 msk
= (BIT29
| BIT28
| BIT27
| BIT26
| BIT25
| BIT24
);
74 tempD
= pi_count
<< 24;
75 isbM32m(DDRPHY
, reg
, tempD
, msk
);
78 // BL0/1 -> B01DBCTL1[08/11] (+1 select)
79 // BL0/1 -> B01DBCTL1[02/05] (enable)
80 reg
= B01DBCTL1
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
84 msk
|= (byte_lane
& BIT0
) ? (BIT5
) : (BIT2
);
85 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
90 msk
|= (byte_lane
& BIT0
) ? (BIT11
) : (BIT8
);
91 if (pi_count
< EARLY_DB
)
95 isbM32m(DDRPHY
, reg
, tempD
, msk
);
100 training_message(channel
, rank
, byte_lane
);
101 post_code(0xEE, 0xE0);
110 // This function will return the current RCVEN delay on the given channel, rank, byte_lane as an absolute PI count.
111 // (currently doesn't comprehend rank)
123 // RDPTR (1/2 MCLK, 64 PIs)
124 // BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
125 // BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
126 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
127 tempD
= isbR32m(DDRPHY
, reg
);
128 tempD
>>= (byte_lane
& BIT0
) ? (20) : (8);
132 pi_count
= tempD
* HALF_CLK
;
134 // PI (1/64 MCLK, 1 PIs)
135 // BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
136 // BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
137 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
138 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
139 tempD
= isbR32m(DDRPHY
, reg
);
152 // This function will program the RDQS delays based on an absolute amount of PIs.
153 // (currently doesn't comprehend rank)
165 DPF(D_TRN
, "Rdqs ch%d rnk%d ln%d : pi=%03X\n", channel
, rank
, byte_lane
, pi_count
);
168 // BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
169 // BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
170 reg
= (byte_lane
& BIT0
) ? (B1RXDQSPICODE
) : (B0RXDQSPICODE
);
171 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
172 msk
= (BIT6
| BIT5
| BIT4
| BIT3
| BIT2
| BIT1
| BIT0
);
173 tempD
= pi_count
<< 0;
174 isbM32m(DDRPHY
, reg
, tempD
, msk
);
176 // error check (shouldn't go above 0x3F)
179 training_message(channel
, rank
, byte_lane
);
180 post_code(0xEE, 0xE1);
189 // This function will return the current RDQS delay on the given channel, rank, byte_lane as an absolute PI count.
190 // (currently doesn't comprehend rank)
203 // BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
204 // BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
205 reg
= (byte_lane
& BIT0
) ? (B1RXDQSPICODE
) : (B0RXDQSPICODE
);
206 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
207 tempD
= isbR32m(DDRPHY
, reg
);
210 pi_count
= tempD
& 0x7F;
218 // This function will program the WDQS delays based on an absolute amount of PIs.
219 // (currently doesn't comprehend rank)
231 DPF(D_TRN
, "Wdqs ch%d rnk%d ln%d : pi=%03X\n", channel
, rank
, byte_lane
, pi_count
);
233 // RDPTR (1/2 MCLK, 64 PIs)
234 // BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
235 // BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
236 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
237 msk
= (byte_lane
& BIT0
) ? (BIT19
| BIT18
| BIT17
| BIT16
) : (BIT7
| BIT6
| BIT5
| BIT4
);
238 tempD
= pi_count
/ HALF_CLK
;
239 tempD
<<= (byte_lane
& BIT0
) ? (16) : (4);
240 isbM32m(DDRPHY
, reg
, tempD
, msk
);
243 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
245 // PI (1/64 MCLK, 1 PIs)
246 // BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
247 // BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
248 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
249 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
250 msk
= (BIT21
| BIT20
| BIT19
| BIT18
| BIT17
| BIT16
);
251 tempD
= pi_count
<< 16;
252 isbM32m(DDRPHY
, reg
, tempD
, msk
);
255 // BL0/1 -> B01DBCTL1[07/10] (+1 select)
256 // BL0/1 -> B01DBCTL1[01/04] (enable)
257 reg
= B01DBCTL1
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
261 msk
|= (byte_lane
& BIT0
) ? (BIT4
) : (BIT1
);
262 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
267 msk
|= (byte_lane
& BIT0
) ? (BIT10
) : (BIT7
);
268 if (pi_count
< EARLY_DB
)
272 isbM32m(DDRPHY
, reg
, tempD
, msk
);
277 training_message(channel
, rank
, byte_lane
);
278 post_code(0xEE, 0xE2);
287 // This function will return the amount of WDQS delay on the given channel, rank, byte_lane as an absolute PI count.
288 // (currently doesn't comprehend rank)
300 // RDPTR (1/2 MCLK, 64 PIs)
301 // BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
302 // BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
303 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
304 tempD
= isbR32m(DDRPHY
, reg
);
305 tempD
>>= (byte_lane
& BIT0
) ? (16) : (4);
309 pi_count
= (tempD
* HALF_CLK
);
311 // PI (1/64 MCLK, 1 PIs)
312 // BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
313 // BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
314 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
315 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
316 tempD
= isbR32m(DDRPHY
, reg
);
329 // This function will program the WDQ delays based on an absolute number of PIs.
330 // (currently doesn't comprehend rank)
342 DPF(D_TRN
, "Wdq ch%d rnk%d ln%d : pi=%03X\n", channel
, rank
, byte_lane
, pi_count
);
344 // RDPTR (1/2 MCLK, 64 PIs)
345 // BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
346 // BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
347 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
348 msk
= (byte_lane
& BIT0
) ? (BIT15
| BIT14
| BIT13
| BIT12
) : (BIT3
| BIT2
| BIT1
| BIT0
);
349 tempD
= pi_count
/ HALF_CLK
;
350 tempD
<<= (byte_lane
& BIT0
) ? (12) : (0);
351 isbM32m(DDRPHY
, reg
, tempD
, msk
);
354 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
356 // PI (1/64 MCLK, 1 PIs)
357 // BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
358 // BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
359 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
360 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
361 msk
= (BIT13
| BIT12
| BIT11
| BIT10
| BIT9
| BIT8
);
362 tempD
= pi_count
<< 8;
363 isbM32m(DDRPHY
, reg
, tempD
, msk
);
366 // BL0/1 -> B01DBCTL1[06/09] (+1 select)
367 // BL0/1 -> B01DBCTL1[00/03] (enable)
368 reg
= B01DBCTL1
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
372 msk
|= (byte_lane
& BIT0
) ? (BIT3
) : (BIT0
);
373 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
378 msk
|= (byte_lane
& BIT0
) ? (BIT9
) : (BIT6
);
379 if (pi_count
< EARLY_DB
)
383 isbM32m(DDRPHY
, reg
, tempD
, msk
);
388 training_message(channel
, rank
, byte_lane
);
389 post_code(0xEE, 0xE3);
398 // This function will return the amount of WDQ delay on the given channel, rank, byte_lane as an absolute PI count.
399 // (currently doesn't comprehend rank)
411 // RDPTR (1/2 MCLK, 64 PIs)
412 // BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
413 // BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
414 reg
= B01PTRCTL0
+ ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
);
415 tempD
= isbR32m(DDRPHY
, reg
);
416 tempD
>>= (byte_lane
& BIT0
) ? (12) : (0);
420 pi_count
= (tempD
* HALF_CLK
);
422 // PI (1/64 MCLK, 1 PIs)
423 // BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
424 // BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
425 reg
= (byte_lane
& BIT0
) ? (B1DLLPICODER0
) : (B0DLLPICODER0
);
426 reg
+= (((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
));
427 tempD
= isbR32m(DDRPHY
, reg
);
440 // This function will program the WCMD delays based on an absolute number of PIs.
450 // RDPTR (1/2 MCLK, 64 PIs)
451 // CMDPTRREG[11:08] (0x0-0xF)
452 reg
= CMDPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
453 msk
= (BIT11
| BIT10
| BIT9
| BIT8
);
454 tempD
= pi_count
/ HALF_CLK
;
456 isbM32m(DDRPHY
, reg
, tempD
, msk
);
459 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
461 // PI (1/64 MCLK, 1 PIs)
462 // CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
463 // CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
464 // CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
465 // CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
466 // CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
467 // CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
468 // CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
469 // CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
470 reg
= CMDDLLPICODER1
+ (channel
* DDRIOCCC_CH_OFFSET
);
472 msk
= (BIT29
| BIT28
| BIT27
| BIT26
| BIT25
| BIT24
) | (BIT21
| BIT20
| BIT19
| BIT18
| BIT17
| BIT16
)
473 | (BIT13
| BIT12
| BIT11
| BIT10
| BIT9
| BIT8
) | (BIT5
| BIT4
| BIT3
| BIT2
| BIT1
| BIT0
);
475 tempD
= (pi_count
<< 24) | (pi_count
<< 16) | (pi_count
<< 8) | (pi_count
<< 0);
477 isbM32m(DDRPHY
, reg
, tempD
, msk
);
478 reg
= CMDDLLPICODER0
+ (channel
* DDRIOCCC_CH_OFFSET
); // PO
479 isbM32m(DDRPHY
, reg
, tempD
, msk
);
482 // CMDCFGREG0[17] (+1 select)
483 // CMDCFGREG0[16] (enable)
484 reg
= CMDCFGREG0
+ (channel
* DDRIOCCC_CH_OFFSET
);
489 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
495 if (pi_count
< EARLY_DB
)
499 isbM32m(DDRPHY
, reg
, tempD
, msk
);
504 post_code(0xEE, 0xE4);
513 // This function will return the amount of WCMD delay on the given channel as an absolute PI count.
522 // RDPTR (1/2 MCLK, 64 PIs)
523 // CMDPTRREG[11:08] (0x0-0xF)
524 reg
= CMDPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
525 tempD
= isbR32m(DDRPHY
, reg
);
530 pi_count
= tempD
* HALF_CLK
;
532 // PI (1/64 MCLK, 1 PIs)
533 // CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
534 // CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
535 // CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
536 // CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
537 // CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
538 // CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
539 // CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
540 // CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
541 reg
= CMDDLLPICODER1
+ (channel
* DDRIOCCC_CH_OFFSET
);
542 tempD
= isbR32m(DDRPHY
, reg
);
555 // This function will program the WCLK delays based on an absolute number of PIs.
566 // RDPTR (1/2 MCLK, 64 PIs)
567 // CCPTRREG[15:12] -> CLK1 (0x0-0xF)
568 // CCPTRREG[11:08] -> CLK0 (0x0-0xF)
569 reg
= CCPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
570 msk
= (BIT15
| BIT14
| BIT13
| BIT12
) | (BIT11
| BIT10
| BIT9
| BIT8
);
571 tempD
= ((pi_count
/ HALF_CLK
) << 12) | ((pi_count
/ HALF_CLK
) << 8);
572 isbM32m(DDRPHY
, reg
, tempD
, msk
);
575 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
577 // PI (1/64 MCLK, 1 PIs)
578 // ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
579 // ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
580 reg
= (rank
) ? (ECCB1DLLPICODER0
) : (ECCB1DLLPICODER0
);
581 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
582 msk
= (BIT21
| BIT20
| BIT19
| BIT18
| BIT17
| BIT16
) | (BIT13
| BIT12
| BIT11
| BIT10
| BIT9
| BIT8
);
583 tempD
= (pi_count
<< 16) | (pi_count
<< 8);
584 isbM32m(DDRPHY
, reg
, tempD
, msk
);
585 reg
= (rank
) ? (ECCB1DLLPICODER1
) : (ECCB1DLLPICODER1
);
586 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
587 isbM32m(DDRPHY
, reg
, tempD
, msk
);
588 reg
= (rank
) ? (ECCB1DLLPICODER2
) : (ECCB1DLLPICODER2
);
589 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
590 isbM32m(DDRPHY
, reg
, tempD
, msk
);
591 reg
= (rank
) ? (ECCB1DLLPICODER3
) : (ECCB1DLLPICODER3
);
592 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
593 isbM32m(DDRPHY
, reg
, tempD
, msk
);
596 // CCCFGREG1[11:08] (+1 select)
597 // CCCFGREG1[03:00] (enable)
598 reg
= CCCFGREG1
+ (channel
* DDRIOCCC_CH_OFFSET
);
602 msk
|= (BIT3
| BIT2
| BIT1
| BIT0
); // only ??? matters
603 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
608 msk
|= (BIT11
| BIT10
| BIT9
| BIT8
); // only ??? matters
609 if (pi_count
< EARLY_DB
)
613 isbM32m(DDRPHY
, reg
, tempD
, msk
);
618 post_code(0xEE, 0xE5);
627 // This function will return the amout of WCLK delay on the given channel, rank as an absolute PI count.
637 // RDPTR (1/2 MCLK, 64 PIs)
638 // CCPTRREG[15:12] -> CLK1 (0x0-0xF)
639 // CCPTRREG[11:08] -> CLK0 (0x0-0xF)
640 reg
= CCPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
641 tempD
= isbR32m(DDRPHY
, reg
);
642 tempD
>>= (rank
) ? (12) : (8);
646 pi_count
= tempD
* HALF_CLK
;
648 // PI (1/64 MCLK, 1 PIs)
649 // ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
650 // ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
651 reg
= (rank
) ? (ECCB1DLLPICODER0
) : (ECCB1DLLPICODER0
);
652 reg
+= (channel
* DDRIOCCC_CH_OFFSET
);
653 tempD
= isbR32m(DDRPHY
, reg
);
654 tempD
>>= (rank
) ? (16) : (8);
665 // This function will program the WCTL delays based on an absolute number of PIs.
666 // (currently doesn't comprehend rank)
678 // RDPTR (1/2 MCLK, 64 PIs)
679 // CCPTRREG[31:28] (0x0-0xF)
680 // CCPTRREG[27:24] (0x0-0xF)
681 reg
= CCPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
682 msk
= (BIT31
| BIT30
| BIT29
| BIT28
) | (BIT27
| BIT26
| BIT25
| BIT24
);
683 tempD
= ((pi_count
/ HALF_CLK
) << 28) | ((pi_count
/ HALF_CLK
) << 24);
684 isbM32m(DDRPHY
, reg
, tempD
, msk
);
687 pi_count
-= ((pi_count
/ HALF_CLK
) & 0xF) * HALF_CLK
;
689 // PI (1/64 MCLK, 1 PIs)
690 // ECCB1DLLPICODER?[29:24] (0x00-0x3F)
691 // ECCB1DLLPICODER?[29:24] (0x00-0x3F)
692 reg
= ECCB1DLLPICODER0
+ (channel
* DDRIOCCC_CH_OFFSET
);
693 msk
= (BIT29
| BIT28
| BIT27
| BIT26
| BIT25
| BIT24
);
694 tempD
= (pi_count
<< 24);
695 isbM32m(DDRPHY
, reg
, tempD
, msk
);
696 reg
= ECCB1DLLPICODER1
+ (channel
* DDRIOCCC_CH_OFFSET
);
697 isbM32m(DDRPHY
, reg
, tempD
, msk
);
698 reg
= ECCB1DLLPICODER2
+ (channel
* DDRIOCCC_CH_OFFSET
);
699 isbM32m(DDRPHY
, reg
, tempD
, msk
);
700 reg
= ECCB1DLLPICODER3
+ (channel
* DDRIOCCC_CH_OFFSET
);
701 isbM32m(DDRPHY
, reg
, tempD
, msk
);
704 // CCCFGREG1[13:12] (+1 select)
705 // CCCFGREG1[05:04] (enable)
706 reg
= CCCFGREG1
+ (channel
* DDRIOCCC_CH_OFFSET
);
710 msk
|= (BIT5
| BIT4
); // only ??? matters
711 if ((pi_count
< EARLY_DB
) || (pi_count
> LATE_DB
))
716 msk
|= (BIT13
| BIT12
); // only ??? matters
717 if (pi_count
< EARLY_DB
)
721 isbM32m(DDRPHY
, reg
, tempD
, msk
);
726 post_code(0xEE, 0xE6);
735 // This function will return the amount of WCTL delay on the given channel, rank as an absolute PI count.
736 // (currently doesn't comprehend rank)
747 // RDPTR (1/2 MCLK, 64 PIs)
748 // CCPTRREG[31:28] (0x0-0xF)
749 // CCPTRREG[27:24] (0x0-0xF)
750 reg
= CCPTRREG
+ (channel
* DDRIOCCC_CH_OFFSET
);
751 tempD
= isbR32m(DDRPHY
, reg
);
756 pi_count
= tempD
* HALF_CLK
;
758 // PI (1/64 MCLK, 1 PIs)
759 // ECCB1DLLPICODER?[29:24] (0x00-0x3F)
760 // ECCB1DLLPICODER?[29:24] (0x00-0x3F)
761 reg
= ECCB1DLLPICODER0
+ (channel
* DDRIOCCC_CH_OFFSET
);
762 tempD
= isbR32m(DDRPHY
, reg
);
775 // This function will program the internal Vref setting in a given byte lane in a given channel.
781 uint32_t reg
= (byte_lane
& 0x1) ? (B1VREFCTL
) : (B0VREFCTL
);
784 DPF(D_TRN
, "Vref ch%d ln%d : val=%03X\n", channel
, byte_lane
, setting
);
786 isbM32m(DDRPHY
, (reg
+ (channel
* DDRIODQ_CH_OFFSET
) + ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
)),
787 (vref_codes
[setting
] << 2), (BIT7
| BIT6
| BIT5
| BIT4
| BIT3
| BIT2
));
788 //isbM32m(DDRPHY, (reg + (channel * DDRIODQ_CH_OFFSET) + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET)), (setting<<2), (BIT7|BIT6|BIT5|BIT4|BIT3|BIT2));
789 // need to wait ~300ns for Vref to settle (check that this is necessary)
791 // ??? may need to clear pointers ???
798 // This function will return the internal Vref setting for the given channel, byte_lane;
804 uint32_t ret_val
= sizeof(vref_codes
) / 2;
805 uint32_t reg
= (byte_lane
& 0x1) ? (B1VREFCTL
) : (B0VREFCTL
);
810 tempD
= isbR32m(DDRPHY
, (reg
+ (channel
* DDRIODQ_CH_OFFSET
) + ((byte_lane
>> 1) * DDRIODQ_BL_OFFSET
)));
813 for (j
= 0; j
< sizeof(vref_codes
); j
++)
815 if (vref_codes
[j
] == tempD
)
827 // This function will be used to clear the pointers in a given byte lane in a given channel.
835 for (channel_i
= 0; channel_i
< NUM_CHANNELS
; channel_i
++)
837 for (bl_i
= 0; bl_i
< NUM_BYTE_LANES
; bl_i
++)
839 isbM32m(DDRPHY
, (B01PTRCTL1
+ (channel_i
* DDRIODQ_CH_OFFSET
) + ((bl_i
>> 1) * DDRIODQ_BL_OFFSET
)), ~(BIT8
),
841 //delay_m(1); // DEBUG
842 isbM32m(DDRPHY
, (B01PTRCTL1
+ (channel_i
* DDRIODQ_CH_OFFSET
) + ((bl_i
>> 1) * DDRIODQ_BL_OFFSET
)), (BIT8
),
850 // void enable_cache:
854 // Cache control not used in Quark MRC
858 // void disable_cache:
862 // Cache control not used in Quark MRC
866 // Send DRAM command, data should be formated
867 // using DCMD_Xxxx macro or emrsXCommand structure.
868 static void dram_init_command(
876 // This function will find the rising edge transition on RCVN or WDQS.
877 void find_rising_edge(
878 MRCParams_t
*mrc_params
,
885 #define SAMPLE_CNT 3 // number of sample points
886 #define SAMPLE_DLY 26 // number of PIs to increment per sample
887 #define FORWARD true // indicates to increase delays when looking for edge
888 #define BACKWARD false // indicates to decrease delays when looking for edge
890 bool all_edges_found
; // determines stop condition
891 bool direction
[NUM_BYTE_LANES
]; // direction indicator
892 uint8_t sample_i
; // sample counter
893 uint8_t bl_i
; // byte lane counter
894 uint8_t bl_divisor
= (mrc_params
->channel_width
== x16
) ? 2 : 1; // byte lane divisor
895 uint32_t sample_result
[SAMPLE_CNT
]; // results of "sample_dqs()"
896 uint32_t tempD
; // temporary DWORD
897 uint32_t transition_pattern
;
901 // select hte and request initial configuration
902 select_hte(mrc_params
);
905 // Take 3 sample points (T1,T2,T3) to obtain a transition pattern.
906 for (sample_i
= 0; sample_i
< SAMPLE_CNT
; sample_i
++)
908 // program the desired delays for sample
909 for (bl_i
= 0; bl_i
< (NUM_BYTE_LANES
/ bl_divisor
); bl_i
++)
911 // increase sample delay by 26 PI (0.2 CLK)
914 set_rcvn(channel
, rank
, bl_i
, delay
[bl_i
] + (sample_i
* SAMPLE_DLY
));
918 set_wdqs(channel
, rank
, bl_i
, delay
[bl_i
] + (sample_i
* SAMPLE_DLY
));
921 // take samples (Tsample_i)
922 sample_result
[sample_i
] = sample_dqs(mrc_params
, channel
, rank
, rcvn
);
924 DPF(D_TRN
, "Find rising edge %s ch%d rnk%d: #%d dly=%d dqs=%02X\n",
925 (rcvn
? "RCVN" : "WDQS"), channel
, rank
,
926 sample_i
, sample_i
* SAMPLE_DLY
, sample_result
[sample_i
]);
930 // This pattern will help determine where we landed and ultimately how to place RCVEN/WDQS.
931 for (bl_i
= 0; bl_i
< (NUM_BYTE_LANES
/ bl_divisor
); bl_i
++)
933 // build "transition_pattern" (MSB is 1st sample)
934 transition_pattern
= 0x00;
935 for (sample_i
= 0; sample_i
< SAMPLE_CNT
; sample_i
++)
937 transition_pattern
|= ((sample_result
[sample_i
] & (1 << bl_i
)) >> bl_i
) << (SAMPLE_CNT
- 1 - sample_i
);
940 DPF(D_TRN
, "=== transition pattern %d\n", transition_pattern
);
942 // set up to look for rising edge based on "transition_pattern"
943 switch (transition_pattern
)
945 case 0x00: // sampled 0->0->0
946 // move forward from T3 looking for 0->1
947 delay
[bl_i
] += 2 * SAMPLE_DLY
;
948 direction
[bl_i
] = FORWARD
;
950 case 0x01: // sampled 0->0->1
951 case 0x05: // sampled 1->0->1 (bad duty cycle) *HSD#237503*
952 // move forward from T2 looking for 0->1
953 delay
[bl_i
] += 1 * SAMPLE_DLY
;
954 direction
[bl_i
] = FORWARD
;
957 // case 0x02: // sampled 0->1->0 (bad duty cycle)
958 // training_message(channel, rank, bl_i);
959 // post_code(0xEE, 0xE8);
961 case 0x02: // sampled 0->1->0 (bad duty cycle) *HSD#237503*
962 case 0x03: // sampled 0->1->1
963 // move forward from T1 looking for 0->1
964 delay
[bl_i
] += 0 * SAMPLE_DLY
;
965 direction
[bl_i
] = FORWARD
;
967 case 0x04: // sampled 1->0->0 (assumes BL8, HSD#234975)
968 // move forward from T3 looking for 0->1
969 delay
[bl_i
] += 2 * SAMPLE_DLY
;
970 direction
[bl_i
] = FORWARD
;
973 // case 0x05: // sampled 1->0->1 (bad duty cycle)
974 // training_message(channel, rank, bl_i);
975 // post_code(0xEE, 0xE9);
977 case 0x06: // sampled 1->1->0
978 case 0x07: // sampled 1->1->1
979 // move backward from T1 looking for 1->0
980 delay
[bl_i
] += 0 * SAMPLE_DLY
;
981 direction
[bl_i
] = BACKWARD
;
984 post_code(0xEE, 0xEE);
986 } // transition_pattern switch
990 set_rcvn(channel
, rank
, bl_i
, delay
[bl_i
]);
994 set_wdqs(channel
, rank
, bl_i
, delay
[bl_i
]);
998 // Based on the observed transition pattern on the byte lane,
999 // begin looking for a rising edge with single PI granularity.
1002 all_edges_found
= true; // assume all byte lanes passed
1003 tempD
= sample_dqs(mrc_params
, channel
, rank
, rcvn
); // take a sample
1004 // check all each byte lane for proper edge
1005 for (bl_i
= 0; bl_i
< (NUM_BYTE_LANES
/ bl_divisor
); bl_i
++)
1007 if (tempD
& (1 << bl_i
))
1010 if (direction
[bl_i
] == BACKWARD
)
1012 // keep looking for edge on this byte lane
1013 all_edges_found
= false;
1017 set_rcvn(channel
, rank
, bl_i
, delay
[bl_i
]);
1021 set_wdqs(channel
, rank
, bl_i
, delay
[bl_i
]);
1028 if (direction
[bl_i
] == FORWARD
)
1030 // keep looking for edge on this byte lane
1031 all_edges_found
= false;
1035 set_rcvn(channel
, rank
, bl_i
, delay
[bl_i
]);
1039 set_wdqs(channel
, rank
, bl_i
, delay
[bl_i
]);
1044 } while (!all_edges_found
);
1046 // restore DDR idle state
1047 dram_init_command(DCMD_PREA(rank
));
1049 DPF(D_TRN
, "Delay %03X %03X %03X %03X\n",
1050 delay
[0], delay
[1], delay
[2], delay
[3]);
1058 // This function will sample the DQTRAINSTS registers in the given channel/rank SAMPLE_SIZE times looking for a valid '0' or '1'.
1059 // It will return an encoded DWORD in which each bit corresponds to the sampled value on the byte lane.
1060 uint32_t sample_dqs(
1061 MRCParams_t
*mrc_params
,
1066 uint8_t j
; // just a counter
1067 uint8_t bl_i
; // which BL in the module (always 2 per module)
1068 uint8_t bl_grp
; // which BL module
1069 uint8_t bl_divisor
= (mrc_params
->channel_width
== x16
) ? 2 : 1; // byte lane divisor
1070 uint32_t msk
[2]; // BLx in module
1071 uint32_t sampled_val
[SAMPLE_SIZE
]; // DQTRAINSTS register contents for each sample
1072 uint32_t num_0s
; // tracks the number of '0' samples
1073 uint32_t num_1s
; // tracks the number of '1' samples
1074 uint32_t ret_val
= 0x00; // assume all '0' samples
1075 uint32_t address
= get_addr(mrc_params
, channel
, rank
);
1077 // initialise "msk[]"
1078 msk
[0] = (rcvn
) ? (BIT1
) : (BIT9
); // BL0
1079 msk
[1] = (rcvn
) ? (BIT0
) : (BIT8
); // BL1
1082 // cycle through each byte lane group
1083 for (bl_grp
= 0; bl_grp
< (NUM_BYTE_LANES
/ bl_divisor
) / 2; bl_grp
++)
1085 // take SAMPLE_SIZE samples
1086 for (j
= 0; j
< SAMPLE_SIZE
; j
++)
1088 HteMemOp(address
, first_run
, rcvn
?0:1);
1091 // record the contents of the proper DQTRAINSTS register
1092 sampled_val
[j
] = isbR32m(DDRPHY
, (DQTRAINSTS
+ (bl_grp
* DDRIODQ_BL_OFFSET
) + (channel
* DDRIODQ_CH_OFFSET
)));
1094 // look for a majority value ( (SAMPLE_SIZE/2)+1 ) on the byte lane
1095 // and set that value in the corresponding "ret_val" bit
1096 for (bl_i
= 0; bl_i
< 2; bl_i
++)
1098 num_0s
= 0x00; // reset '0' tracker for byte lane
1099 num_1s
= 0x00; // reset '1' tracker for byte lane
1100 for (j
= 0; j
< SAMPLE_SIZE
; j
++)
1102 if (sampled_val
[j
] & msk
[bl_i
])
1111 if (num_1s
> num_0s
)
1113 ret_val
|= (1 << (bl_i
+ (bl_grp
* 2)));
1118 // "ret_val.0" contains the status of BL0
1119 // "ret_val.1" contains the status of BL1
1120 // "ret_val.2" contains the status of BL2
1127 // This function will return a 32 bit address in the desired channel and rank.
1129 MRCParams_t
*mrc_params
,
1133 uint32_t offset
= 0x02000000; // 32MB
1135 // Begin product specific code
1138 DPF(D_ERROR
, "ILLEGAL CHANNEL\n");
1144 DPF(D_ERROR
, "ILLEGAL RANK\n");
1148 // use 256MB lowest density as per DRP == 0x0003
1149 offset
+= rank
* (256 * 1024 * 1024);
1156 // This function will return a 32 bit mask that will be used to check for byte lane failures.
1157 uint32_t byte_lane_mask(
1158 MRCParams_t
*mrc_params
)
1161 uint32_t ret_val
= 0x00;
1163 // set "ret_val" based on NUM_BYTE_LANES such that you will check only BL0 in "result"
1164 // (each bit in "result" represents a byte lane)
1165 for (j
= 0; j
< MAX_BYTE_LANES
; j
+= NUM_BYTE_LANES
)
1167 ret_val
|= (1 << ((j
/ NUM_BYTE_LANES
) * NUM_BYTE_LANES
));
1171 // need to adjust the mask for 16-bit mode
1172 if (mrc_params
->channel_width
== x16
)
1174 ret_val
|= (ret_val
<< 2);
1183 // This function will do some assembly to return TSC register contents as a uint64_t.
1187 volatile uint64_t tsc
; // EDX:EAX
1189 #if defined (SIM) || defined (GCC)
1190 volatile uint32_t tscH
; // EDX
1191 volatile uint32_t tscL
;// EAX
1193 asm("rdtsc":"=a"(tscL
),"=d"(tscH
));
1195 tsc
= (tsc
<<32)|tscL
;
1205 // This function returns the TSC frequency in MHz
1206 uint32_t get_tsc_freq(
1209 static uint32_t freq
[] =
1210 { 533, 400, 200, 100 };
1213 fuse
= (isbR32m(FUSE
, 0) >> 12) & (BIT1
|BIT0
);
1215 // todo!!! Fixed 533MHz for emulation or debugging
1224 // This is a simple delay function.
1225 // It takes "nanoseconds" as a parameter.
1227 uint32_t nanoseconds
)
1229 // 1000 MHz clock has 1ns period --> no conversion required
1230 uint64_t final_tsc
= read_tsc();
1231 final_tsc
+= ((get_tsc_freq() * (nanoseconds
)) / 1000);
1233 while (read_tsc() < final_tsc
)
1241 // This is a simple delay function.
1242 // It takes "microseconds as a parameter.
1244 uint32_t microseconds
)
1246 // 64 bit math is not an option, just use loops
1247 while (microseconds
--)
1256 // This is a simple delay function.
1257 // It takes "milliseconds" as a parameter.
1259 uint32_t milliseconds
)
1261 // 64 bit math is not an option, just use loops
1262 while (milliseconds
--)
1271 // This is a simple delay function.
1272 // It takes "seconds" as a parameter.
1276 // 64 bit math is not an option, just use loops
1286 // This function will output the POST CODE to the four 7-Segment LED displays.
1292 // Update global variable for execution tracking in debug env
1293 PostCode
= ((major
<< 8) | minor
);
1296 // send message to UART
1297 DPF(D_INFO
, "POST: 0x%01X%02X\n", major
, minor
);
1302 // todo!!! Consider updating error status and exit MRC
1304 // enable Ctrl-C handling
1305 for(;;) delay_n(100);
1312 void training_message(
1317 // send message to UART
1318 DPF(D_INFO
, "CH%01X RK%01X BL%01X\n", channel
, rank
, byte_lane
);
1323 MRCParams_t
*mrc_params
)
1329 uint8_t bl_divisor
= (mrc_params
->channel_width
== x16
) ? 2 : 1;
1331 DPF(D_INFO
, "\n---------------------------");
1332 DPF(D_INFO
, "\nALGO[CH:RK] BL0 BL1 BL2 BL3");
1333 DPF(D_INFO
, "\n===========================");
1334 for (algo_i
= 0; algo_i
< eMAX_ALGOS
; algo_i
++)
1336 for (channel_i
= 0; channel_i
< NUM_CHANNELS
; channel_i
++)
1338 if (mrc_params
->channel_enables
& (1 << channel_i
))
1340 for (rank_i
= 0; rank_i
< NUM_RANKS
; rank_i
++)
1342 if (mrc_params
->rank_enables
& (1 << rank_i
))
1347 DPF(D_INFO
, "\nRCVN[%02d:%02d]", channel_i
, rank_i
);
1350 DPF(D_INFO
, "\nWDQS[%02d:%02d]", channel_i
, rank_i
);
1353 DPF(D_INFO
, "\nWDQx[%02d:%02d]", channel_i
, rank_i
);
1356 DPF(D_INFO
, "\nRDQS[%02d:%02d]", channel_i
, rank_i
);
1359 DPF(D_INFO
, "\nVREF[%02d:%02d]", channel_i
, rank_i
);
1362 DPF(D_INFO
, "\nWCMD[%02d:%02d]", channel_i
, rank_i
);
1365 DPF(D_INFO
, "\nWCTL[%02d:%02d]", channel_i
, rank_i
);
1368 DPF(D_INFO
, "\nWCLK[%02d:%02d]", channel_i
, rank_i
);
1373 for (bl_i
= 0; bl_i
< (NUM_BYTE_LANES
/ bl_divisor
); bl_i
++)
1378 DPF(D_INFO
, " %03d", get_rcvn(channel_i
, rank_i
, bl_i
));
1381 DPF(D_INFO
, " %03d", get_wdqs(channel_i
, rank_i
, bl_i
));
1384 DPF(D_INFO
, " %03d", get_wdq(channel_i
, rank_i
, bl_i
));
1387 DPF(D_INFO
, " %03d", get_rdqs(channel_i
, rank_i
, bl_i
));
1390 DPF(D_INFO
, " %03d", get_vref(channel_i
, bl_i
));
1393 DPF(D_INFO
, " %03d", get_wcmd(channel_i
));
1396 DPF(D_INFO
, " %03d", get_wctl(channel_i
, rank_i
));
1399 DPF(D_INFO
, " %03d", get_wclk(channel_i
, rank_i
));
1405 } // if rank_i enabled
1407 } // if channel_i enabled
1410 DPF(D_INFO
, "\n---------------------------");
1415 // 32 bit LFSR with characteristic polynomial: X^32 + X^22 +X^2 + X^1
1416 // The function takes pointer to previous 32 bit value and modifies it to next value.
1426 for (i
= 0; i
< 32; i
++)
1428 bit
= 1 ^ (lfsr
& BIT0
);
1429 bit
= bit
^ ((lfsr
& BIT1
) >> 1);
1430 bit
= bit
^ ((lfsr
& BIT2
) >> 2);
1431 bit
= bit
^ ((lfsr
& BIT22
) >> 22);
1433 lfsr
= ((lfsr
>> 1) | (bit
<< 31));
1440 // The purpose of this function is to ensure the SEC comes out of reset
1441 // and IA initiates the SEC enabling Memory Scrambling.
1442 void enable_scrambling(
1443 MRCParams_t
*mrc_params
)
1448 if (mrc_params
->scrambling_enables
== 0)
1453 // 32 bit seed is always stored in BIOS NVM.
1454 lfsr
= mrc_params
->timings
.scrambler_seed
;
1456 if (mrc_params
->boot_mode
== bmCold
)
1458 // factory value is 0 and in first boot, a clock based seed is loaded.
1461 lfsr
= read_tsc() & 0x0FFFFFFF; // get seed from system clock and make sure it is not all 1's
1463 // need to replace scrambler
1464 // get next 32bit LFSR 16 times which is the last part of the previous scrambler vector.
1467 for (i
= 0; i
< 16; i
++)
1472 mrc_params
->timings
.scrambler_seed
= lfsr
; // save new seed.
1475 // In warm boot or S3 exit, we have the previous seed.
1476 // In cold boot, we have the last 32bit LFSR which is the new seed.
1477 lfsr32(&lfsr
); // shift to next value
1478 isbW32m(MCU
, SCRMSEED
, (lfsr
& 0x0003FFFF));
1479 for (i
= 0; i
< 2; i
++)
1481 isbW32m(MCU
, SCRMLO
+ i
, (lfsr
& 0xAAAAAAAA));
1488 // This function will store relevant timing data
1489 // This data will be used on subsequent boots to speed up boot times
1490 // and is required for Suspend To RAM capabilities.
1492 MRCParams_t
*mrc_params
)
1495 MrcTimings_t
*mt
= &mrc_params
->timings
;
1497 for (ch
= 0; ch
< NUM_CHANNELS
; ch
++)
1499 for (rk
= 0; rk
< NUM_RANKS
; rk
++)
1501 for (bl
= 0; bl
< NUM_BYTE_LANES
; bl
++)
1503 mt
->rcvn
[ch
][rk
][bl
] = get_rcvn(ch
, rk
, bl
); // RCVN
1504 mt
->rdqs
[ch
][rk
][bl
] = get_rdqs(ch
, rk
, bl
); // RDQS
1505 mt
->wdqs
[ch
][rk
][bl
] = get_wdqs(ch
, rk
, bl
); // WDQS
1506 mt
->wdq
[ch
][rk
][bl
] = get_wdq(ch
, rk
, bl
); // WDQ
1509 mt
->vref
[ch
][bl
] = get_vref(ch
, bl
); // VREF (RANK0 only)
1512 mt
->wctl
[ch
][rk
] = get_wctl(ch
, rk
); // WCTL
1514 mt
->wcmd
[ch
] = get_wcmd(ch
); // WCMD
1517 // need to save for a case of changing frequency after warm reset
1518 mt
->ddr_speed
= mrc_params
->ddr_speed
;
1523 // This function will retrieve relevant timing data
1524 // This data will be used on subsequent boots to speed up boot times
1525 // and is required for Suspend To RAM capabilities.
1526 void restore_timings(
1527 MRCParams_t
*mrc_params
)
1530 const MrcTimings_t
*mt
= &mrc_params
->timings
;
1532 for (ch
= 0; ch
< NUM_CHANNELS
; ch
++)
1534 for (rk
= 0; rk
< NUM_RANKS
; rk
++)
1536 for (bl
= 0; bl
< NUM_BYTE_LANES
; bl
++)
1538 set_rcvn(ch
, rk
, bl
, mt
->rcvn
[ch
][rk
][bl
]); // RCVN
1539 set_rdqs(ch
, rk
, bl
, mt
->rdqs
[ch
][rk
][bl
]); // RDQS
1540 set_wdqs(ch
, rk
, bl
, mt
->wdqs
[ch
][rk
][bl
]); // WDQS
1541 set_wdq(ch
, rk
, bl
, mt
->wdq
[ch
][rk
][bl
]); // WDQ
1544 set_vref(ch
, bl
, mt
->vref
[ch
][bl
]); // VREF (RANK0 only)
1547 set_wctl(ch
, rk
, mt
->wctl
[ch
][rk
]); // WCTL
1549 set_wcmd(ch
, mt
->wcmd
[ch
]); // WCMD
1555 // Configure default settings normally set as part of read training
1556 // Some defaults have to be set earlier as they may affect earlier
1558 void default_timings(
1559 MRCParams_t
*mrc_params
)
1563 for (ch
= 0; ch
< NUM_CHANNELS
; ch
++)
1565 for (rk
= 0; rk
< NUM_RANKS
; rk
++)
1567 for (bl
= 0; bl
< NUM_BYTE_LANES
; bl
++)
1569 set_rdqs(ch
, rk
, bl
, 24); // RDQS
1572 set_vref(ch
, bl
, 32); // VREF (RANK0 only)