1 /************************************************************************
2  *
3  * Copyright (c) 2013-2015 Intel Corporation.
4  *
5 * SPDX-License-Identifier: BSD-2-Clause-Patent
6  *
7  * This file contains all of the Cat Mountain Memory Reference Code (MRC).
8  *
9  * These functions are generic and should work for any Cat Mountain config.
10  *
11  * MRC requires two data structures to be passed in which are initialised by "PreMemInit()".
12  *
13  * The basic flow is as follows:
14  * 01) Check for supported DDR speed configuration
15  * 02) Set up MEMORY_MANAGER buffer as pass-through (POR)
16  * 03) Set Channel Interleaving Mode and Channel Stride to the most aggressive setting possible
17  * 04) Set up the MCU logic
18  * 05) Set up the DDR_PHY logic
19  * 06) Initialise the DRAMs (JEDEC)
20  * 07) Perform the Receive Enable Calibration algorithm
21  * 08) Perform the Write Leveling algorithm
22  * 09) Perform the Read Training algorithm (includes internal Vref)
23  * 10) Perform the Write Training algorithm
24  * 11) Set Channel Interleaving Mode and Channel Stride to the desired settings
25  *
26  * Dunit configuration based on Valleyview MRC.
27  *
28  ***************************************************************************/
29 
30 #include "mrc.h"
31 #include "memory_options.h"
32 
33 #include "meminit.h"
34 #include "meminit_utils.h"
35 #include "hte.h"
36 #include "io.h"
37 
38 // Override ODT to off state if requested
39 #define DRMC_DEFAULT    (mrc_params->rd_odt_value==0?BIT12:0)
40 
41 
42 // tRFC values (in picoseconds) per density
43 const uint32_t tRFC[5] =
44 {
45     90000,  // 512Mb
46     110000, // 1Gb
47     160000, // 2Gb
48     300000, // 4Gb
49     350000, // 8Gb
50     };
51 
52 // tCK clock period in picoseconds per speed index 800, 1066, 1333
53 const uint32_t tCK[3] =
54 {
55     2500,
56     1875,
57     1500
58 };
59 
60 #ifdef SIM
61 // Select static timings specific to simulation environment
62 #define PLATFORM_ID    0
63 #else
64 // Select static timings specific to ClantonPeek platform
65 #define PLATFORM_ID    1
66 #endif
67 
68 
69 // Global variables
70 const uint16_t ddr_wclk[] =
71     {193, 158};
72 
73 const uint16_t ddr_wctl[] =
74     {  1, 217};
75 
76 const uint16_t ddr_wcmd[] =
77     {  1, 220};
78 
79 
80 #ifdef BACKUP_RCVN
81 const uint16_t ddr_rcvn[] =
82     {129, 498};
83 #endif // BACKUP_RCVN
84 
85 #ifdef BACKUP_WDQS
86 const uint16_t ddr_wdqs[] =
87     { 65, 289};
88 #endif // BACKUP_WDQS
89 
90 #ifdef BACKUP_RDQS
91 const uint8_t ddr_rdqs[] =
92     { 32,  24};
93 #endif // BACKUP_RDQS
94 
95 #ifdef BACKUP_WDQ
96 const uint16_t ddr_wdq[] =
97     { 32, 257};
98 #endif // BACKUP_WDQ
99 
100 
101 
102 // Select MEMORY_MANAGER as the source for PRI interface
select_memory_manager(MRCParams_t * mrc_params)103 static void select_memory_manager(
104     MRCParams_t *mrc_params)
105 {
106   RegDCO Dco;
107 
108   ENTERFN();
109 
110   Dco.raw = isbR32m(MCU, DCO);
111   Dco.field.PMICTL = 0;          //0 - PRI owned by MEMORY_MANAGER
112   isbW32m(MCU, DCO, Dco.raw);
113 
114   LEAVEFN();
115 }
116 
117 // Select HTE as the source for PRI interface
select_hte(MRCParams_t * mrc_params)118 void select_hte(
119     MRCParams_t *mrc_params)
120 {
121   RegDCO Dco;
122 
123   ENTERFN();
124 
125   Dco.raw = isbR32m(MCU, DCO);
126   Dco.field.PMICTL = 1;          //1 - PRI owned by HTE
127   isbW32m(MCU, DCO, Dco.raw);
128 
129   LEAVEFN();
130 }
131 
132 // Send DRAM command, data should be formated
133 // using DCMD_Xxxx macro or emrsXCommand structure.
dram_init_command(uint32_t data)134 static void dram_init_command(
135     uint32_t data)
136 {
137   Wr32(DCMD, 0, data);
138 }
139 
140 // Send DRAM wake command using special MCU side-band WAKE opcode
dram_wake_command(void)141 static void dram_wake_command(
142     void)
143 {
144   ENTERFN();
145 
146   Wr32(MMIO, PCIADDR(0,0,0,SB_PACKET_REG),
147       (uint32_t) SB_COMMAND(SB_WAKE_CMND_OPCODE, MCU, 0));
148 
149   LEAVEFN();
150 }
151 
152 // Stop self refresh driven by MCU
clear_self_refresh(MRCParams_t * mrc_params)153 static void clear_self_refresh(
154     MRCParams_t *mrc_params)
155 {
156   ENTERFN();
157 
158   // clear the PMSTS Channel Self Refresh bits
159   isbM32m(MCU, PMSTS, BIT0, BIT0);
160 
161   LEAVEFN();
162 }
163 
164 // Configure MCU before jedec init sequence
prog_decode_before_jedec(MRCParams_t * mrc_params)165 static void prog_decode_before_jedec(
166     MRCParams_t *mrc_params)
167 {
168   RegDRP Drp;
169   RegDRCF Drfc;
170   RegDCAL Dcal;
171   RegDSCH Dsch;
172   RegDPMC0 Dpmc0;
173 
174   ENTERFN();
175 
176   // Disable power saving features
177   Dpmc0.raw = isbR32m(MCU, DPMC0);
178   Dpmc0.field.CLKGTDIS = 1;
179   Dpmc0.field.DISPWRDN = 1;
180   Dpmc0.field.DYNSREN = 0;
181   Dpmc0.field.PCLSTO = 0;
182   isbW32m(MCU, DPMC0, Dpmc0.raw);
183 
184   // Disable out of order transactions
185   Dsch.raw = isbR32m(MCU, DSCH);
186   Dsch.field.OOODIS = 1;
187   Dsch.field.NEWBYPDIS = 1;
188   isbW32m(MCU, DSCH, Dsch.raw);
189 
190   // Disable issuing the REF command
191   Drfc.raw = isbR32m(MCU, DRFC);
192   Drfc.field.tREFI = 0;
193   isbW32m(MCU, DRFC, Drfc.raw);
194 
195   // Disable ZQ calibration short
196   Dcal.raw = isbR32m(MCU, DCAL);
197   Dcal.field.ZQCINT = 0;
198   Dcal.field.SRXZQCL = 0;
199   isbW32m(MCU, DCAL, Dcal.raw);
200 
201   // Training performed in address mode 0, rank population has limited impact, however
202   // simulator complains if enabled non-existing rank.
203   Drp.raw = 0;
204   if (mrc_params->rank_enables & 1)
205     Drp.field.rank0Enabled = 1;
206   if (mrc_params->rank_enables & 2)
207     Drp.field.rank1Enabled = 1;
208   isbW32m(MCU, DRP, Drp.raw);
209 
210   LEAVEFN();
211 }
212 
213 // After Cold Reset, BIOS should set COLDWAKE bit to 1 before
214 // sending the WAKE message to the Dunit.
215 // For Standby Exit, or any other mode in which the DRAM is in
216 // SR, this bit must be set to 0.
perform_ddr_reset(MRCParams_t * mrc_params)217 static void perform_ddr_reset(
218     MRCParams_t *mrc_params)
219 {
220   ENTERFN();
221 
222   // Set COLDWAKE bit before sending the WAKE message
223   isbM32m(MCU, DRMC, BIT16, BIT16);
224 
225   // Send wake command to DUNIT (MUST be done before JEDEC)
226   dram_wake_command();
227 
228   // Set default value
229   isbW32m(MCU, DRMC, DRMC_DEFAULT);
230 
231   LEAVEFN();
232 }
233 
234 // Dunit Initialisation Complete.
235 // Indicates that initialisation of the Dunit has completed.
236 // Memory accesses are permitted and maintenance operation
237 // begins. Until this bit is set to a 1, the memory controller will
238 // not accept DRAM requests from the MEMORY_MANAGER or HTE.
set_ddr_init_complete(MRCParams_t * mrc_params)239 static void set_ddr_init_complete(
240     MRCParams_t *mrc_params)
241 {
242   RegDCO Dco;
243 
244   ENTERFN();
245 
246   Dco.raw = isbR32m(MCU, DCO);
247   Dco.field.PMICTL = 0;          //0 - PRI owned by MEMORY_MANAGER
248   Dco.field.IC = 1;              //1 - initialisation complete
249   isbW32m(MCU, DCO, Dco.raw);
250 
251   LEAVEFN();
252 }
253 
prog_page_ctrl(MRCParams_t * mrc_params)254 static void prog_page_ctrl(
255     MRCParams_t *mrc_params)
256 {
257   RegDPMC0 Dpmc0;
258 
259   ENTERFN();
260 
261   Dpmc0.raw = isbR32m(MCU, DPMC0);
262 
263   Dpmc0.field.PCLSTO = 0x4;
264   Dpmc0.field.PREAPWDEN = 1;
265 
266   isbW32m(MCU, DPMC0, Dpmc0.raw);
267 }
268 
269 // Configure MCU Power Management Control Register
270 // and Scheduler Control Register.
prog_ddr_control(MRCParams_t * mrc_params)271 static void prog_ddr_control(
272     MRCParams_t *mrc_params)
273 {
274   RegDSCH Dsch;
275   RegDPMC0 Dpmc0;
276 
277   ENTERFN();
278 
279   Dpmc0.raw = isbR32m(MCU, DPMC0);
280   Dsch.raw = isbR32m(MCU, DSCH);
281 
282   Dpmc0.field.DISPWRDN = mrc_params->power_down_disable;
283   Dpmc0.field.CLKGTDIS = 0;
284   Dpmc0.field.PCLSTO = 4;
285   Dpmc0.field.PREAPWDEN = 1;
286 
287   Dsch.field.OOODIS = 0;
288   Dsch.field.OOOST3DIS = 0;
289   Dsch.field.NEWBYPDIS = 0;
290 
291   isbW32m(MCU, DSCH, Dsch.raw);
292   isbW32m(MCU, DPMC0, Dpmc0.raw);
293 
294   // CMDTRIST = 2h - CMD/ADDR are tristated when no valid command
295   isbM32m(MCU, DPMC1, 2 << 4, BIT5|BIT4);
296 
297   LEAVEFN();
298 }
299 
300 // After training complete configure MCU Rank Population Register
301 // specifying: ranks enabled, device width, density, address mode.
prog_dra_drb(MRCParams_t * mrc_params)302 static void prog_dra_drb(
303     MRCParams_t *mrc_params)
304 {
305   RegDRP Drp;
306   RegDCO Dco;
307 
308   ENTERFN();
309 
310   Dco.raw = isbR32m(MCU, DCO);
311   Dco.field.IC = 0;
312   isbW32m(MCU, DCO, Dco.raw);
313 
314   Drp.raw = 0;
315   if (mrc_params->rank_enables & 1)
316     Drp.field.rank0Enabled = 1;
317   if (mrc_params->rank_enables & 2)
318     Drp.field.rank1Enabled = 1;
319   if (mrc_params->dram_width == x16)
320   {
321     Drp.field.dimm0DevWidth = 1;
322     Drp.field.dimm1DevWidth = 1;
323   }
324   // Density encoding in DRAMParams_t 0=512Mb, 1=Gb, 2=2Gb, 3=4Gb
325   // has to be mapped RANKDENSx encoding (0=1Gb)
326   Drp.field.dimm0DevDensity = mrc_params->params.DENSITY - 1;
327   Drp.field.dimm1DevDensity = mrc_params->params.DENSITY - 1;
328 
329   // Address mode can be overwritten if ECC enabled
330   Drp.field.addressMap = mrc_params->address_mode;
331 
332   isbW32m(MCU, DRP, Drp.raw);
333 
334   Dco.field.PMICTL = 0;          //0 - PRI owned by MEMORY_MANAGER
335   Dco.field.IC = 1;              //1 - initialisation complete
336   isbW32m(MCU, DCO, Dco.raw);
337 
338   LEAVEFN();
339 }
340 
341 // Configure refresh rate and short ZQ calibration interval.
342 // Activate dynamic self refresh.
change_refresh_period(MRCParams_t * mrc_params)343 static void change_refresh_period(
344     MRCParams_t *mrc_params)
345 {
346   RegDRCF Drfc;
347   RegDCAL Dcal;
348   RegDPMC0 Dpmc0;
349 
350   ENTERFN();
351 
352   Drfc.raw = isbR32m(MCU, DRFC);
353   Drfc.field.tREFI = mrc_params->refresh_rate;
354   Drfc.field.REFDBTCLR = 1;
355   isbW32m(MCU, DRFC, Drfc.raw);
356 
357   Dcal.raw = isbR32m(MCU, DCAL);
358   Dcal.field.ZQCINT = 3; // 63ms
359   isbW32m(MCU, DCAL, Dcal.raw);
360 
361   Dpmc0.raw = isbR32m(MCU, DPMC0);
362   Dpmc0.field.ENPHYCLKGATE = 1;
363   Dpmc0.field.DYNSREN = 1;
364   isbW32m(MCU, DPMC0, Dpmc0.raw);
365 
366   LEAVEFN();
367 }
368 
369 // Send DRAM wake command
perform_wake(MRCParams_t * mrc_params)370 static void perform_wake(
371     MRCParams_t *mrc_params)
372 {
373   ENTERFN();
374 
375   dram_wake_command();
376 
377   LEAVEFN();
378 }
379 
380 // prog_ddr_timing_control (aka mcu_init):
381 // POST_CODE[major] == 0x02
382 //
383 // It will initialise timing registers in the MCU (DTR0..DTR4).
prog_ddr_timing_control(MRCParams_t * mrc_params)384 static void prog_ddr_timing_control(
385     MRCParams_t *mrc_params)
386 {
387   uint8_t TCL, WL;
388   uint8_t TRP, TRCD, TRAS, TWR, TWTR, TRRD, TRTP, TFAW;
389   uint32_t TCK;
390 
391   RegDTR0 Dtr0;
392   RegDTR1 Dtr1;
393   RegDTR2 Dtr2;
394   RegDTR3 Dtr3;
395   RegDTR4 Dtr4;
396 
397   ENTERFN();
398 
399   // mcu_init starts
400   post_code(0x02, 0x00);
401 
402   Dtr0.raw = isbR32m(MCU, DTR0);
403   Dtr1.raw = isbR32m(MCU, DTR1);
404   Dtr2.raw = isbR32m(MCU, DTR2);
405   Dtr3.raw = isbR32m(MCU, DTR3);
406   Dtr4.raw = isbR32m(MCU, DTR4);
407 
408   TCK = tCK[mrc_params->ddr_speed];  // Clock in picoseconds
409   TCL = mrc_params->params.tCL;      // CAS latency in clocks
410   TRP = TCL;  // Per CAT MRC
411   TRCD = TCL;  // Per CAT MRC
412   TRAS = MCEIL(mrc_params->params.tRAS, TCK);
413   TWR = MCEIL(15000, TCK);   // Per JEDEC: tWR=15000ps DDR2/3 from 800-1600
414 
415   TWTR = MCEIL(mrc_params->params.tWTR, TCK);
416   TRRD = MCEIL(mrc_params->params.tRRD, TCK);
417   TRTP = 4;  // Valid for 800 and 1066, use 5 for 1333
418   TFAW = MCEIL(mrc_params->params.tFAW, TCK);
419 
420   WL = 5 + mrc_params->ddr_speed;
421 
422   Dtr0.field.dramFrequency = mrc_params->ddr_speed;
423 
424   Dtr0.field.tCL = TCL - 5;            //Convert from TCL (DRAM clocks) to VLV indx
425   Dtr0.field.tRP = TRP - 5;            //5 bit DRAM Clock
426   Dtr0.field.tRCD = TRCD - 5;          //5 bit DRAM Clock
427 
428   Dtr1.field.tWCL = WL - 3;            //Convert from WL (DRAM clocks)  to VLV indx
429   Dtr1.field.tWTP = WL + 4 + TWR - 14;  //Change to tWTP
430   Dtr1.field.tRTP = MMAX(TRTP, 4) - 3;  //4 bit DRAM Clock
431   Dtr1.field.tRRD = TRRD - 4;        //4 bit DRAM Clock
432   Dtr1.field.tCMD = 1;             //2N
433   Dtr1.field.tRAS = TRAS - 14;      //6 bit DRAM Clock
434 
435   Dtr1.field.tFAW = ((TFAW + 1) >> 1) - 5;    //4 bit DRAM Clock
436   Dtr1.field.tCCD = 0;                        //Set 4 Clock CAS to CAS delay (multi-burst)
437   Dtr2.field.tRRDR = 1;
438   Dtr2.field.tWWDR = 2;
439   Dtr2.field.tRWDR = 2;
440   Dtr3.field.tWRDR = 2;
441   Dtr3.field.tWRDD = 2;
442 
443   if (mrc_params->ddr_speed == DDRFREQ_800)
444   {
445      // Extended RW delay (+1)
446      Dtr3.field.tRWSR = TCL - 5 + 1;
447   }
448   else if(mrc_params->ddr_speed == DDRFREQ_1066)
449   {
450      // Extended RW delay (+1)
451      Dtr3.field.tRWSR = TCL - 5 + 1;
452   }
453 
454   Dtr3.field.tWRSR = 4 + WL + TWTR - 11;
455 
456   if (mrc_params->ddr_speed == DDRFREQ_800)
457   {
458     Dtr3.field.tXP = MMAX(0, 1 - Dtr1.field.tCMD);
459   }
460   else
461   {
462     Dtr3.field.tXP = MMAX(0, 2 - Dtr1.field.tCMD);
463   }
464 
465   Dtr4.field.WRODTSTRT = Dtr1.field.tCMD;
466   Dtr4.field.WRODTSTOP = Dtr1.field.tCMD;
467   Dtr4.field.RDODTSTRT = Dtr1.field.tCMD + Dtr0.field.tCL - Dtr1.field.tWCL + 2; //Convert from WL (DRAM clocks)  to VLV indx
468   Dtr4.field.RDODTSTOP = Dtr1.field.tCMD + Dtr0.field.tCL - Dtr1.field.tWCL + 2;
469   Dtr4.field.TRGSTRDIS = 0;
470   Dtr4.field.ODTDIS = 0;
471 
472   isbW32m(MCU, DTR0, Dtr0.raw);
473   isbW32m(MCU, DTR1, Dtr1.raw);
474   isbW32m(MCU, DTR2, Dtr2.raw);
475   isbW32m(MCU, DTR3, Dtr3.raw);
476   isbW32m(MCU, DTR4, Dtr4.raw);
477 
478   LEAVEFN();
479 }
480 
481 // ddrphy_init:
482 // POST_CODE[major] == 0x03
483 //
484 // This function performs some initialisation on the DDRIO unit.
485 // This function is dependent on BOARD_ID, DDR_SPEED, and CHANNEL_ENABLES.
ddrphy_init(MRCParams_t * mrc_params)486 static void ddrphy_init(MRCParams_t *mrc_params)
487 {
488   uint32_t tempD; // temporary DWORD
489   uint8_t channel_i; // channel counter
490   uint8_t rank_i; // rank counter
491   uint8_t bl_grp_i; // byte lane group counter (2 BLs per module)
492 
493   uint8_t bl_divisor = /*(mrc_params->channel_width==x16)?2:*/1; // byte lane divisor
494   uint8_t speed = mrc_params->ddr_speed & (BIT1|BIT0); // For DDR3 --> 0 == 800, 1 == 1066, 2 == 1333
495   uint8_t tCAS;
496   uint8_t tCWL;
497 
498   ENTERFN();
499 
500   tCAS = mrc_params->params.tCL;
501   tCWL = 5 + mrc_params->ddr_speed;
502 
503   // ddrphy_init starts
504   post_code(0x03, 0x00);
505 
506   // HSD#231531
507   // Make sure IOBUFACT is deasserted before initialising the DDR PHY.
508   // HSD#234845
509   // Make sure WRPTRENABLE is deasserted before initialising the DDR PHY.
510   for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
511     if (mrc_params->channel_enables & (1<<channel_i)) {
512       // Deassert DDRPHY Initialisation Complete
513       isbM32m(DDRPHY, (CMDPMCONFIG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ~BIT20, BIT20); // SPID_INIT_COMPLETE=0
514       // Deassert IOBUFACT
515       isbM32m(DDRPHY, (CMDCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ~BIT2, BIT2); // IOBUFACTRST_N=0
516       // Disable WRPTR
517       isbM32m(DDRPHY, (CMDPTRREG + (channel_i * DDRIOCCC_CH_OFFSET)), ~BIT0, BIT0); // WRPTRENABLE=0
518     } // if channel enabled
519   } // channel_i loop
520 
521   // Put PHY in reset
522   isbM32m(DDRPHY, MASTERRSTN, 0, BIT0); // PHYRSTN=0
523 
524   // Initialise DQ01,DQ23,CMD,CLK-CTL,COMP modules
525   // STEP0:
526   post_code(0x03, 0x10);
527   for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
528     if (mrc_params->channel_enables & (1<<channel_i)) {
529 
530       // DQ01-DQ23
531       for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
532         isbM32m(DDRPHY, (DQOBSCKEBBCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((bl_grp_i) ? (0x00) : (BIT22)), (BIT22)); // Analog MUX select - IO2xCLKSEL
533 
534         // ODT Strength
535         switch (mrc_params->rd_odt_value) {
536           case 1: tempD = 0x3; break; // 60 ohm
537           case 2: tempD = 0x3; break; // 120 ohm
538           case 3: tempD = 0x3; break; // 180 ohm
539           default: tempD = 0x3; break; // 120 ohm
540         }
541         isbM32m(DDRPHY, (B0RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (tempD<<5), (BIT6|BIT5)); // ODT strength
542         isbM32m(DDRPHY, (B1RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (tempD<<5), (BIT6|BIT5)); // ODT strength
543         // Dynamic ODT/DIFFAMP
544         tempD = (((tCAS)<<24)|((tCAS)<<16)|((tCAS)<<8)|((tCAS)<<0));
545         switch (speed) {
546           case 0: tempD -= 0x01010101; break; // 800
547           case 1: tempD -= 0x02020202; break; // 1066
548           case 2: tempD -= 0x03030303; break; // 1333
549           case 3: tempD -= 0x04040404; break; // 1600
550         }
551         isbM32m(DDRPHY, (B01LATCTL1 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // Launch Time: ODT, DIFFAMP, ODT, DIFFAMP
552         switch (speed) {
553           // HSD#234715
554           case 0: tempD = ((0x06<<16)|(0x07<<8)); break; // 800
555           case 1: tempD = ((0x07<<16)|(0x08<<8)); break; // 1066
556           case 2: tempD = ((0x09<<16)|(0x0A<<8)); break; // 1333
557           case 3: tempD = ((0x0A<<16)|(0x0B<<8)); break; // 1600
558         }
559         isbM32m(DDRPHY, (B0ONDURCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT13|BIT12|BIT11|BIT10|BIT9|BIT8))); // On Duration: ODT, DIFFAMP
560         isbM32m(DDRPHY, (B1ONDURCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT13|BIT12|BIT11|BIT10|BIT9|BIT8))); // On Duration: ODT, DIFFAMP
561 
562         switch (mrc_params->rd_odt_value) {
563           case 0:  tempD = ((0x3F<<16)|(0x3f<<10)); break; // override DIFFAMP=on, ODT=off
564           default: tempD = ((0x3F<<16)|(0x2A<<10)); break; // override DIFFAMP=on, ODT=on
565         }
566         isbM32m(DDRPHY, (B0OVRCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10))); // Override: DIFFAMP, ODT
567         isbM32m(DDRPHY, (B1OVRCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10))); // Override: DIFFAMP, ODT
568 
569         // DLL Setup
570         // 1xCLK Domain Timings: tEDP,RCVEN,WDQS (PO)
571         isbM32m(DDRPHY, (B0LATCTL0 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (((tCAS+7)<<16)|((tCAS-4)<<8)|((tCWL-2)<<0)), ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // 1xCLK: tEDP, RCVEN, WDQS
572         isbM32m(DDRPHY, (B1LATCTL0 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (((tCAS+7)<<16)|((tCAS-4)<<8)|((tCWL-2)<<0)), ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // 1xCLK: tEDP, RCVEN, WDQS
573 
574         // RCVEN Bypass (PO)
575         isbM32m(DDRPHY, (B0RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x0<<7)|(0x0<<0)), (BIT7|BIT0)); // AFE Bypass, RCVEN DIFFAMP
576         isbM32m(DDRPHY, (B1RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x0<<7)|(0x0<<0)), (BIT7|BIT0)); // AFE Bypass, RCVEN DIFFAMP
577         // TX
578         isbM32m(DDRPHY, (DQCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT16), (BIT16)); // 0 means driving DQ during DQS-preamble
579         isbM32m(DDRPHY, (B01PTRCTL1 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT8), (BIT8)); // WR_LVL mode disable
580         // RX (PO)
581         isbM32m(DDRPHY, (B0VREFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x03<<2)|(0x0<<1)|(0x0<<0)), ((BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)|BIT1|BIT0)); // Internal Vref Code, Enable#, Ext_or_Int (1=Ext)
582         isbM32m(DDRPHY, (B1VREFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x03<<2)|(0x0<<1)|(0x0<<0)), ((BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)|BIT1|BIT0)); // Internal Vref Code, Enable#, Ext_or_Int (1=Ext)
583         isbM32m(DDRPHY, (B0RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (0), (BIT4)); // Per-Bit De-Skew Enable
584         isbM32m(DDRPHY, (B1RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (0), (BIT4)); // Per-Bit De-Skew Enable
585       }
586       // CLKEBB
587       isbM32m(DDRPHY, (CMDOBSCKEBBCTL + (channel_i * DDRIOCCC_CH_OFFSET)), 0, (BIT23));
588 
589       // Enable tristate control of cmd/address bus
590       isbM32m(DDRPHY, (CMDCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), 0, (BIT1|BIT0));
591 
592       // ODT RCOMP
593       isbM32m(DDRPHY, (CMDRCOMPODT + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x03<<5)|(0x03<<0)), ((BIT9|BIT8|BIT7|BIT6|BIT5)|(BIT4|BIT3|BIT2|BIT1|BIT0)));
594 
595       // CMDPM* registers must be programmed in this order...
596       isbM32m(DDRPHY, (CMDPMDLYREG4 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFFFU<<16)|(0xFFFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8|BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Turn On Delays: SFR (regulator), MPLL
597       isbM32m(DDRPHY, (CMDPMDLYREG3 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFU<<28)|(0xFFF<<16)|(0xF<<12)|(0x616<<0)), ((BIT31|BIT30|BIT29|BIT28)|(BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8|BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Delays: ASSERT_IOBUFACT_to_ALLON0_for_PM_MSG_3, VREG (MDLL) Turn On, ALLON0_to_DEASSERT_IOBUFACT_for_PM_MSG_gt0, MDLL Turn On
598       isbM32m(DDRPHY, (CMDPMDLYREG2 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFU<<24)|(0xFF<<16)|(0xFF<<8)|(0xFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // MPLL Divider Reset Delays
599       isbM32m(DDRPHY, (CMDPMDLYREG1 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFU<<24)|(0xFF<<16)|(0xFF<<8)|(0xFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Turn Off Delays: VREG, Staggered MDLL, MDLL, PI
600       isbM32m(DDRPHY, (CMDPMDLYREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFU<<24)|(0xFF<<16)|(0xFF<<8)|(0xFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Turn On Delays: MPLL, Staggered MDLL, PI, IOBUFACT
601       isbM32m(DDRPHY, (CMDPMCONFIG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x6<<8)|BIT6|(0x4<<0)), (BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|(BIT11|BIT10|BIT9|BIT8)|BIT6|(BIT3|BIT2|BIT1|BIT0))); // Allow PUnit signals
602       isbM32m(DDRPHY, (CMDMDLLCTL +   (channel_i * DDRIOCCC_CH_OFFSET)), ((0x3<<4)|(0x7<<0)), ((BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // DLL_VREG Bias Trim, VREF Tuning for DLL_VREG
603       // CLK-CTL
604       isbM32m(DDRPHY, (CCOBSCKEBBCTL + (channel_i * DDRIOCCC_CH_OFFSET)), 0, (BIT24)); // CLKEBB
605       isbM32m(DDRPHY, (CCCFGREG0 +     (channel_i * DDRIOCCC_CH_OFFSET)), ((0x0<<16)|(0x0<<12)|(0x0<<8)|(0xF<<4)|BIT0), ((BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|BIT0)); // Buffer Enable: CS,CKE,ODT,CLK
606       isbM32m(DDRPHY, (CCRCOMPODT +    (channel_i * DDRIOCCC_CH_OFFSET)), ((0x03<<8)|(0x03<<0)), ((BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT RCOMP
607       isbM32m(DDRPHY, (CCMDLLCTL +     (channel_i * DDRIOCCC_CH_OFFSET)), ((0x3<<4)|(0x7<<0)), ((BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // DLL_VREG Bias Trim, VREF Tuning for DLL_VREG
608 
609       // COMP (RON channel specific)
610       // - DQ/DQS/DM RON: 32 Ohm
611       // - CTRL/CMD RON: 27 Ohm
612       // - CLK RON: 26 Ohm
613       isbM32m(DDRPHY, (DQVREFCH0 +  (channel_i * DDRCOMP_CH_OFFSET)), ((0x08<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)));  // RCOMP Vref PU/PD
614       isbM32m(DDRPHY, (CMDVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0C<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)));  // RCOMP Vref PU/PD
615       isbM32m(DDRPHY, (CLKVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0F<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)));  // RCOMP Vref PU/PD
616       isbM32m(DDRPHY, (DQSVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x08<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)));  // RCOMP Vref PU/PD
617       isbM32m(DDRPHY, (CTLVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0C<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)));  // RCOMP Vref PU/PD
618 
619       // DQS Swapped Input Enable
620       isbM32m(DDRPHY, (COMPEN1CH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT19|BIT17),           ((BIT31|BIT30)|BIT19|BIT17|(BIT15|BIT14)));
621 
622       // ODT VREF = 1.5 x 274/360+274 = 0.65V (code of ~50)
623       isbM32m(DDRPHY, (DQVREFCH0 +  (channel_i * DDRCOMP_CH_OFFSET)), ((0x32<<8)|(0x03<<0)),   ((BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT Vref PU/PD
624       isbM32m(DDRPHY, (DQSVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x32<<8)|(0x03<<0)),   ((BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT Vref PU/PD
625       isbM32m(DDRPHY, (CLKVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0E<<8)|(0x05<<0)),   ((BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT Vref PU/PD
626 
627       // Slew rate settings are frequency specific, numbers below are for 800Mhz (speed == 0)
628       // - DQ/DQS/DM/CLK SR: 4V/ns,
629       // - CTRL/CMD SR: 1.5V/ns
630       tempD = (0x0E<<16)|(0x0E<<12)|(0x08<<8)|(0x0B<<4)|(0x0B<<0);
631       isbM32m(DDRPHY, (DLYSELCH0 +   (channel_i * DDRCOMP_CH_OFFSET)), (tempD), ((BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // DCOMP Delay Select: CTL,CMD,CLK,DQS,DQ
632       isbM32m(DDRPHY, (TCOVREFCH0 +  (channel_i * DDRCOMP_CH_OFFSET)), ((0x05<<16)|(0x05<<8)|(0x05<<0)), ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // TCO Vref CLK,DQS,DQ
633       isbM32m(DDRPHY, (CCBUFODTCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x03<<8)|(0x03<<0)), ((BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // ODTCOMP CMD/CTL PU/PD
634       isbM32m(DDRPHY, (COMPEN0CH0 +  (channel_i * DDRCOMP_CH_OFFSET)), (0), ((BIT31|BIT30)|BIT8)); // COMP
635 
636       #ifdef BACKUP_COMPS
637       // DQ COMP Overrides
638       isbM32m(DDRPHY, (DQDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
639       isbM32m(DDRPHY, (DQDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
640       isbM32m(DDRPHY, (DQDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
641       isbM32m(DDRPHY, (DQDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
642       isbM32m(DDRPHY, (DQODTPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PU
643       isbM32m(DDRPHY, (DQODTPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PD
644       isbM32m(DDRPHY, (DQTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PU
645       isbM32m(DDRPHY, (DQTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PD
646       // DQS COMP Overrides
647       isbM32m(DDRPHY, (DQSDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
648       isbM32m(DDRPHY, (DQSDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
649       isbM32m(DDRPHY, (DQSDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
650       isbM32m(DDRPHY, (DQSDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
651       isbM32m(DDRPHY, (DQSODTPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PU
652       isbM32m(DDRPHY, (DQSODTPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PD
653       isbM32m(DDRPHY, (DQSTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PU
654       isbM32m(DDRPHY, (DQSTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PD
655       // CLK COMP Overrides
656       isbM32m(DDRPHY, (CLKDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0C<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
657       isbM32m(DDRPHY, (CLKDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0C<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
658       isbM32m(DDRPHY, (CLKDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x07<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
659       isbM32m(DDRPHY, (CLKDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x07<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
660       isbM32m(DDRPHY, (CLKODTPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PU
661       isbM32m(DDRPHY, (CLKODTPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PD
662       isbM32m(DDRPHY, (CLKTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PU
663       isbM32m(DDRPHY, (CLKTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PD
664       // CMD COMP Overrides
665       isbM32m(DDRPHY, (CMDDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
666       isbM32m(DDRPHY, (CMDDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
667       isbM32m(DDRPHY, (CMDDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
668       isbM32m(DDRPHY, (CMDDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
669       // CTL COMP Overrides
670       isbM32m(DDRPHY, (CTLDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
671       isbM32m(DDRPHY, (CTLDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
672       isbM32m(DDRPHY, (CTLDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
673       isbM32m(DDRPHY, (CTLDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
674       #else
675       // DQ TCOCOMP Overrides
676       isbM32m(DDRPHY, (DQTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PU
677       isbM32m(DDRPHY, (DQTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PD
678       // DQS TCOCOMP Overrides
679       isbM32m(DDRPHY, (DQSTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PU
680       isbM32m(DDRPHY, (DQSTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PD
681       // CLK TCOCOMP Overrides
682       isbM32m(DDRPHY, (CLKTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PU
683       isbM32m(DDRPHY, (CLKTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PD
684       #endif // BACKUP_COMPS
685       // program STATIC delays
686       #ifdef BACKUP_WCMD
687       set_wcmd(channel_i, ddr_wcmd[PLATFORM_ID]);
688       #else
689       set_wcmd(channel_i, ddr_wclk[PLATFORM_ID] + HALF_CLK);
690       #endif // BACKUP_WCMD
691       for (rank_i=0; rank_i<NUM_RANKS; rank_i++) {
692         if (mrc_params->rank_enables & (1<<rank_i)) {
693           set_wclk(channel_i, rank_i, ddr_wclk[PLATFORM_ID]);
694           #ifdef BACKUP_WCTL
695           set_wctl(channel_i, rank_i, ddr_wctl[PLATFORM_ID]);
696           #else
697           set_wctl(channel_i, rank_i, ddr_wclk[PLATFORM_ID] + HALF_CLK);
698           #endif // BACKUP_WCTL
699         }
700       }
701     }
702   }
703   // COMP (non channel specific)
704   //isbM32m(DDRPHY, (), (), ());
705   isbM32m(DDRPHY, (DQANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
706   isbM32m(DDRPHY, (DQANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
707   isbM32m(DDRPHY, (CMDANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
708   isbM32m(DDRPHY, (CMDANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
709   isbM32m(DDRPHY, (CLKANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
710   isbM32m(DDRPHY, (CLKANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
711   isbM32m(DDRPHY, (DQSANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
712   isbM32m(DDRPHY, (DQSANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
713   isbM32m(DDRPHY, (CTLANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
714   isbM32m(DDRPHY, (CTLANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
715   isbM32m(DDRPHY, (DQANAODTPUCTL), (BIT30), (BIT30)); // ODT: Dither PU Enable
716   isbM32m(DDRPHY, (DQANAODTPDCTL), (BIT30), (BIT30)); // ODT: Dither PD Enable
717   isbM32m(DDRPHY, (CLKANAODTPUCTL), (BIT30), (BIT30)); // ODT: Dither PU Enable
718   isbM32m(DDRPHY, (CLKANAODTPDCTL), (BIT30), (BIT30)); // ODT: Dither PD Enable
719   isbM32m(DDRPHY, (DQSANAODTPUCTL), (BIT30), (BIT30)); // ODT: Dither PU Enable
720   isbM32m(DDRPHY, (DQSANAODTPDCTL), (BIT30), (BIT30)); // ODT: Dither PD Enable
721   isbM32m(DDRPHY, (DQANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
722   isbM32m(DDRPHY, (DQANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
723   isbM32m(DDRPHY, (CMDANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
724   isbM32m(DDRPHY, (CMDANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
725   isbM32m(DDRPHY, (CLKANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
726   isbM32m(DDRPHY, (CLKANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
727   isbM32m(DDRPHY, (DQSANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
728   isbM32m(DDRPHY, (DQSANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
729   isbM32m(DDRPHY, (CTLANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
730   isbM32m(DDRPHY, (CTLANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
731   isbM32m(DDRPHY, (DQANATCOPUCTL), (BIT30), (BIT30)); // TCO: Dither PU Enable
732   isbM32m(DDRPHY, (DQANATCOPDCTL), (BIT30), (BIT30)); // TCO: Dither PD Enable
733   isbM32m(DDRPHY, (CLKANATCOPUCTL), (BIT30), (BIT30)); // TCO: Dither PU Enable
734   isbM32m(DDRPHY, (CLKANATCOPDCTL), (BIT30), (BIT30)); // TCO: Dither PD Enable
735   isbM32m(DDRPHY, (DQSANATCOPUCTL), (BIT30), (BIT30)); // TCO: Dither PU Enable
736   isbM32m(DDRPHY, (DQSANATCOPDCTL), (BIT30), (BIT30)); // TCO: Dither PD Enable
737   isbM32m(DDRPHY, (TCOCNTCTRL), (0x1<<0), (BIT1|BIT0)); // TCOCOMP: Pulse Count
738   isbM32m(DDRPHY, (CHNLBUFSTATIC), ((0x03<<24)|(0x03<<16)), ((BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODT: CMD/CTL PD/PU
739   isbM32m(DDRPHY, (MSCNTR), (0x64<<0), (BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0)); // Set 1us counter
740   isbM32m(DDRPHY, (LATCH1CTL), (0x1<<28), (BIT30|BIT29|BIT28)); // ???
741 
742   // Release PHY from reset
743   isbM32m(DDRPHY, MASTERRSTN, BIT0, BIT0); // PHYRSTN=1
744 
745   // STEP1:
746   post_code(0x03, 0x11);
747   for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
748     if (mrc_params->channel_enables & (1<<channel_i)) {
749       // DQ01-DQ23
750       for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
751         isbM32m(DDRPHY, (DQMDLLCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT13), (BIT13)); // Enable VREG
752         delay_n(3);
753       }
754       // ECC
755       isbM32m(DDRPHY, (ECCMDLLCTL), (BIT13), (BIT13)); // Enable VREG
756       delay_n(3);
757       // CMD
758       isbM32m(DDRPHY, (CMDMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT13), (BIT13)); // Enable VREG
759       delay_n(3);
760       // CLK-CTL
761       isbM32m(DDRPHY, (CCMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT13), (BIT13)); // Enable VREG
762       delay_n(3);
763     }
764   }
765 
766   // STEP2:
767   post_code(0x03, 0x12);
768   delay_n(200);
769   for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
770     if (mrc_params->channel_enables & (1<<channel_i)) {
771       // DQ01-DQ23
772       for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
773         isbM32m(DDRPHY, (DQMDLLCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT17), (BIT17)); // Enable MCDLL
774         delay_n(50);
775       }
776       // ECC
777       isbM32m(DDRPHY, (ECCMDLLCTL), (BIT17), (BIT17)); // Enable MCDLL
778       delay_n(50);
779       // CMD
780       isbM32m(DDRPHY, (CMDMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT18), (BIT18)); // Enable MCDLL
781       delay_n(50);
782       // CLK-CTL
783       isbM32m(DDRPHY, (CCMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT18), (BIT18)); // Enable MCDLL
784       delay_n(50);
785     }
786   }
787 
788   // STEP3:
789   post_code(0x03, 0x13);
790   delay_n(100);
791   for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
792     if (mrc_params->channel_enables & (1<<channel_i)) {
793       // DQ01-DQ23
794       for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
795 #ifdef FORCE_16BIT_DDRIO
796         tempD = ((bl_grp_i) && (mrc_params->channel_width == x16)) ? ((0x1<<12)|(0x1<<8)|(0xF<<4)|(0xF<<0)) : ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0));
797 #else
798         tempD = ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0));
799 #endif
800         isbM32m(DDRPHY, (DQDLLTXCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (tempD), ((BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // Enable TXDLL
801         delay_n(3);
802         isbM32m(DDRPHY, (DQDLLRXCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT3|BIT2|BIT1|BIT0), (BIT3|BIT2|BIT1|BIT0)); // Enable RXDLL
803         delay_n(3);
804         isbM32m(DDRPHY, (B0OVRCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT3|BIT2|BIT1|BIT0), (BIT3|BIT2|BIT1|BIT0)); // Enable RXDLL Overrides BL0
805       }
806 
807       // ECC
808       tempD = ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0));
809       isbM32m(DDRPHY, (ECCDLLTXCTL), (tempD), ((BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // Enable TXDLL
810       delay_n(3);
811 
812       // CMD (PO)
813       isbM32m(DDRPHY, (CMDDLLTXCTL + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0)), ((BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // Enable TXDLL
814       delay_n(3);
815     }
816   }
817 
818 
819   // STEP4:
820   post_code(0x03, 0x14);
821   for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
822     if (mrc_params->channel_enables & (1<<channel_i)) {
823       // Host To Memory Clock Alignment (HMC) for 800/1066
824       for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
825         isbM32m(DDRPHY, (DQCLKALIGNREG2 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((bl_grp_i)?(0x3):(0x1)), (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
826       }
827       isbM32m(DDRPHY, (ECCCLKALIGNREG2 + (channel_i * DDRIODQ_CH_OFFSET)), 0x2, (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
828       isbM32m(DDRPHY, (CMDCLKALIGNREG2 + (channel_i * DDRIODQ_CH_OFFSET)), 0x0, (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
829       isbM32m(DDRPHY, (CCCLKALIGNREG2 + (channel_i * DDRIODQ_CH_OFFSET)), 0x2, (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
830       isbM32m(DDRPHY, (CMDCLKALIGNREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), (0x2<<4), (BIT5|BIT4)); // CLK_ALIGN_MODE
831       isbM32m(DDRPHY, (CMDCLKALIGNREG1 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x18<<16)|(0x10<<8)|(0x8<<2)|(0x1<<0)), ((BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)|(BIT1|BIT0))); // NUM_SAMPLES, MAX_SAMPLES, MACRO_PI_STEP, MICRO_PI_STEP
832       isbM32m(DDRPHY, (CMDCLKALIGNREG2 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x10<<16)|(0x4<<8)|(0x2<<4)), ((BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4))); // ???, TOTAL_NUM_MODULES, FIRST_U_PARTITION
833       #ifdef HMC_TEST
834       isbM32m(DDRPHY, (CMDCLKALIGNREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), BIT24, BIT24); // START_CLK_ALIGN=1
835       while (isbR32m(DDRPHY, (CMDCLKALIGNREG0 + (channel_i * DDRIOCCC_CH_OFFSET))) & BIT24); // wait for START_CLK_ALIGN=0
836       #endif // HMC_TEST
837 
838       // Set RD/WR Pointer Seperation & COUNTEN & FIFOPTREN
839       isbM32m(DDRPHY, (CMDPTRREG + (channel_i * DDRIOCCC_CH_OFFSET)), BIT0, BIT0); // WRPTRENABLE=1
840 
841 
842 #ifdef SIM
843       // comp is not working on simulator
844 #else
845       // COMP initial
846       isbM32m(DDRPHY, (COMPEN0CH0 + (channel_i * DDRCOMP_CH_OFFSET)), BIT5, BIT5); // enable bypass for CLK buffer (PO)
847       isbM32m(DDRPHY, (CMPCTRL), (BIT0), (BIT0)); // Initial COMP Enable
848       while (isbR32m(DDRPHY, (CMPCTRL)) & BIT0); // wait for Initial COMP Enable = 0
849       isbM32m(DDRPHY, (COMPEN0CH0 + (channel_i * DDRCOMP_CH_OFFSET)), ~BIT5, BIT5); // disable bypass for CLK buffer (PO)
850 #endif
851 
852       // IOBUFACT
853       // STEP4a
854       isbM32m(DDRPHY, (CMDCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), BIT2, BIT2); // IOBUFACTRST_N=1
855 
856       // DDRPHY initialisation complete
857       isbM32m(DDRPHY, (CMDPMCONFIG0 + (channel_i * DDRIOCCC_CH_OFFSET)), BIT20, BIT20); // SPID_INIT_COMPLETE=1
858     }
859   }
860 
861   LEAVEFN();
862   return;
863 }
864 
865 // jedec_init (aka PerformJedecInit):
866 // This function performs JEDEC initialisation on all enabled channels.
jedec_init(MRCParams_t * mrc_params,uint32_t silent)867 static void jedec_init(
868     MRCParams_t *mrc_params,
869     uint32_t silent)
870 {
871   uint8_t TWR, WL, Rank;
872   uint32_t TCK;
873 
874   RegDTR0 DTR0reg;
875 
876   DramInitDDR3MRS0 mrs0Command;
877   DramInitDDR3EMR1 emrs1Command;
878   DramInitDDR3EMR2 emrs2Command;
879   DramInitDDR3EMR3 emrs3Command;
880 
881   ENTERFN();
882 
883   // jedec_init starts
884   if (!silent)
885   {
886     post_code(0x04, 0x00);
887   }
888 
889   // Assert RESET# for 200us
890   isbM32m(DDRPHY, CCDDR3RESETCTL, BIT1, (BIT8|BIT1)); // DDR3_RESET_SET=0, DDR3_RESET_RESET=1
891 #ifdef QUICKSIM
892       // Don't waste time during simulation
893       delay_u(2);
894 #else
895   delay_u(200);
896 #endif
897   isbM32m(DDRPHY, CCDDR3RESETCTL, BIT8, (BIT8|BIT1)); // DDR3_RESET_SET=1, DDR3_RESET_RESET=0
898 
899   DTR0reg.raw = isbR32m(MCU, DTR0);
900 
901   // Set CKEVAL for populated ranks
902   // then send NOP to each rank (#4550197)
903   {
904     uint32_t DRPbuffer;
905     uint32_t DRMCbuffer;
906 
907     DRPbuffer = isbR32m(MCU, DRP);
908     DRPbuffer &= 0x3;
909     DRMCbuffer = isbR32m(MCU, DRMC);
910     DRMCbuffer &= 0xFFFFFFFC;
911     DRMCbuffer |= (BIT4 | DRPbuffer);
912 
913     isbW32m(MCU, DRMC, DRMCbuffer);
914 
915     for (Rank = 0; Rank < NUM_RANKS; Rank++)
916     {
917       // Skip to next populated rank
918       if ((mrc_params->rank_enables & (1 << Rank)) == 0)
919       {
920         continue;
921       }
922 
923       dram_init_command(DCMD_NOP(Rank));
924     }
925 
926     isbW32m(MCU, DRMC, DRMC_DEFAULT);
927   }
928 
929   // setup for emrs 2
930   // BIT[15:11] --> Always "0"
931   // BIT[10:09] --> Rtt_WR: want "Dynamic ODT Off" (0)
932   // BIT[08]    --> Always "0"
933   // BIT[07]    --> SRT: use sr_temp_range
934   // BIT[06]    --> ASR: want "Manual SR Reference" (0)
935   // BIT[05:03] --> CWL: use oem_tCWL
936   // BIT[02:00] --> PASR: want "Full Array" (0)
937   emrs2Command.raw = 0;
938   emrs2Command.field.bankAddress = 2;
939 
940   WL = 5 + mrc_params->ddr_speed;
941   emrs2Command.field.CWL = WL - 5;
942   emrs2Command.field.SRT = mrc_params->sr_temp_range;
943 
944   // setup for emrs 3
945   // BIT[15:03] --> Always "0"
946   // BIT[02]    --> MPR: want "Normal Operation" (0)
947   // BIT[01:00] --> MPR_Loc: want "Predefined Pattern" (0)
948   emrs3Command.raw = 0;
949   emrs3Command.field.bankAddress = 3;
950 
951   // setup for emrs 1
952   // BIT[15:13]     --> Always "0"
953   // BIT[12:12]     --> Qoff: want "Output Buffer Enabled" (0)
954   // BIT[11:11]     --> TDQS: want "Disabled" (0)
955   // BIT[10:10]     --> Always "0"
956   // BIT[09,06,02]  --> Rtt_nom: use rtt_nom_value
957   // BIT[08]        --> Always "0"
958   // BIT[07]        --> WR_LVL: want "Disabled" (0)
959   // BIT[05,01]     --> DIC: use ron_value
960   // BIT[04:03]     --> AL: additive latency want "0" (0)
961   // BIT[00]        --> DLL: want "Enable" (0)
962   //
963   // (BIT5|BIT1) set Ron value
964   // 00 --> RZQ/6 (40ohm)
965   // 01 --> RZQ/7 (34ohm)
966   // 1* --> RESERVED
967   //
968   // (BIT9|BIT6|BIT2) set Rtt_nom value
969   // 000 --> Disabled
970   // 001 --> RZQ/4 ( 60ohm)
971   // 010 --> RZQ/2 (120ohm)
972   // 011 --> RZQ/6 ( 40ohm)
973   // 1** --> RESERVED
974   emrs1Command.raw = 0;
975   emrs1Command.field.bankAddress = 1;
976   emrs1Command.field.dllEnabled = 0; // 0 = Enable , 1 = Disable
977 
978   if (mrc_params->ron_value == 0)
979   {
980     emrs1Command.field.DIC0 = DDR3_EMRS1_DIC_34;
981   }
982   else
983   {
984     emrs1Command.field.DIC0 = DDR3_EMRS1_DIC_40;
985   }
986 
987 
988   if (mrc_params->rtt_nom_value == 0)
989   {
990     emrs1Command.raw |= (DDR3_EMRS1_RTTNOM_40 << 6);
991   }
992   else if (mrc_params->rtt_nom_value == 1)
993   {
994     emrs1Command.raw |= (DDR3_EMRS1_RTTNOM_60 << 6);
995   }
996   else if (mrc_params->rtt_nom_value == 2)
997   {
998     emrs1Command.raw |= (DDR3_EMRS1_RTTNOM_120 << 6);
999   }
1000 
1001   // save MRS1 value (excluding control fields)
1002   mrc_params->mrs1 = emrs1Command.raw >> 6;
1003 
1004   // setup for mrs 0
1005   // BIT[15:13]     --> Always "0"
1006   // BIT[12]        --> PPD: for Quark (1)
1007   // BIT[11:09]     --> WR: use oem_tWR
1008   // BIT[08]        --> DLL: want "Reset" (1, self clearing)
1009   // BIT[07]        --> MODE: want "Normal" (0)
1010   // BIT[06:04,02]  --> CL: use oem_tCAS
1011   // BIT[03]        --> RD_BURST_TYPE: want "Interleave" (1)
1012   // BIT[01:00]     --> BL: want "8 Fixed" (0)
1013   // WR:
1014   // 0 --> 16
1015   // 1 --> 5
1016   // 2 --> 6
1017   // 3 --> 7
1018   // 4 --> 8
1019   // 5 --> 10
1020   // 6 --> 12
1021   // 7 --> 14
1022   // CL:
1023   // BIT[02:02] "0" if oem_tCAS <= 11 (1866?)
1024   // BIT[06:04] use oem_tCAS-4
1025   mrs0Command.raw = 0;
1026   mrs0Command.field.bankAddress = 0;
1027   mrs0Command.field.dllReset = 1;
1028   mrs0Command.field.BL = 0;
1029   mrs0Command.field.PPD = 1;
1030   mrs0Command.field.casLatency = DTR0reg.field.tCL + 1;
1031 
1032   TCK = tCK[mrc_params->ddr_speed];
1033   TWR = MCEIL(15000, TCK);   // Per JEDEC: tWR=15000ps DDR2/3 from 800-1600
1034   mrs0Command.field.writeRecovery = TWR - 4;
1035 
1036   for (Rank = 0; Rank < NUM_RANKS; Rank++)
1037   {
1038     // Skip to next populated rank
1039     if ((mrc_params->rank_enables & (1 << Rank)) == 0)
1040     {
1041       continue;
1042     }
1043 
1044     emrs2Command.field.rankSelect = Rank;
1045     dram_init_command(emrs2Command.raw);
1046 
1047     emrs3Command.field.rankSelect = Rank;
1048     dram_init_command(emrs3Command.raw);
1049 
1050     emrs1Command.field.rankSelect = Rank;
1051     dram_init_command(emrs1Command.raw);
1052 
1053     mrs0Command.field.rankSelect = Rank;
1054     dram_init_command(mrs0Command.raw);
1055 
1056     dram_init_command(DCMD_ZQCL(Rank));
1057   }
1058 
1059   LEAVEFN();
1060   return;
1061 }
1062 
1063 // rcvn_cal:
1064 // POST_CODE[major] == 0x05
1065 //
1066 // This function will perform our RCVEN Calibration Algorithm.
1067 // We will only use the 2xCLK domain timings to perform RCVEN Calibration.
1068 // All byte lanes will be calibrated "simultaneously" per channel per rank.
rcvn_cal(MRCParams_t * mrc_params)1069 static void rcvn_cal(
1070     MRCParams_t *mrc_params)
1071 {
1072   uint8_t channel_i; // channel counter
1073   uint8_t rank_i; // rank counter
1074   uint8_t bl_i; // byte lane counter
1075   uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
1076 
1077 #ifdef R2R_SHARING
1078   uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
1079 #ifndef BACKUP_RCVN
1080   uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
1081 #endif // BACKUP_RCVN
1082 #endif // R2R_SHARING
1083 
1084 #ifdef BACKUP_RCVN
1085 #else
1086   uint32_t tempD; // temporary DWORD
1087   uint32_t delay[NUM_BYTE_LANES]; // absolute PI value to be programmed on the byte lane
1088   RegDTR1 dtr1;
1089   RegDTR1 dtr1save;
1090 #endif // BACKUP_RCVN
1091   ENTERFN();
1092 
1093   // rcvn_cal starts
1094   post_code(0x05, 0x00);
1095 
1096 #ifndef BACKUP_RCVN
1097   // need separate burst to sample DQS preamble
1098   dtr1.raw = dtr1save.raw = isbR32m(MCU, DTR1);
1099   dtr1.field.tCCD = 1;
1100   isbW32m(MCU, DTR1, dtr1.raw);
1101 #endif
1102 
1103 #ifdef R2R_SHARING
1104   // need to set "final_delay[][]" elements to "0"
1105   memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
1106 #endif // R2R_SHARING
1107 
1108   // loop through each enabled channel
1109   for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
1110   {
1111     if (mrc_params->channel_enables & (1 << channel_i))
1112     {
1113       // perform RCVEN Calibration on a per rank basis
1114       for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
1115       {
1116         if (mrc_params->rank_enables & (1 << rank_i))
1117         {
1118           // POST_CODE here indicates the current channel and rank being calibrated
1119           post_code(0x05, (0x10 + ((channel_i << 4) | rank_i)));
1120 
1121 #ifdef BACKUP_RCVN
1122           // set hard-coded timing values
1123           for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
1124           {
1125             set_rcvn(channel_i, rank_i, bl_i, ddr_rcvn[PLATFORM_ID]);
1126           }
1127 #else
1128           // enable FIFORST
1129           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i += 2)
1130           {
1131             isbM32m(DDRPHY, (B01PTRCTL1 + ((bl_i >> 1) * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), 0,
1132                 BIT8); // 0 is enabled
1133           } // bl_i loop
1134           // initialise the starting delay to 128 PI (tCAS +1 CLK)
1135           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1136           {
1137 #ifdef SIM
1138             // Original value was late at the end of DQS sequence
1139             delay[bl_i] = 3 * FULL_CLK;
1140 #else
1141             delay[bl_i] = (4 + 1) * FULL_CLK; // 1x CLK domain timing is tCAS-4
1142 #endif
1143 
1144             set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
1145           } // bl_i loop
1146 
1147           // now find the rising edge
1148           find_rising_edge(mrc_params, delay, channel_i, rank_i, true);
1149           // Now increase delay by 32 PI (1/4 CLK) to place in center of high pulse.
1150           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1151           {
1152             delay[bl_i] += QRTR_CLK;
1153             set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
1154           } // bl_i loop
1155           // Now decrement delay by 128 PI (1 CLK) until we sample a "0"
1156           do
1157           {
1158 
1159             tempD = sample_dqs(mrc_params, channel_i, rank_i, true);
1160             for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1161             {
1162               if (tempD & (1 << bl_i))
1163               {
1164                 if (delay[bl_i] >= FULL_CLK)
1165                 {
1166                   delay[bl_i] -= FULL_CLK;
1167                   set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
1168                 }
1169                 else
1170                 {
1171                   // not enough delay
1172                   training_message(channel_i, rank_i, bl_i);
1173                   post_code(0xEE, 0x50);
1174                 }
1175               }
1176             } // bl_i loop
1177           } while (tempD & 0xFF);
1178 
1179 #ifdef R2R_SHARING
1180           // increment "num_ranks_enabled"
1181           num_ranks_enabled++;
1182           // Finally increment delay by 32 PI (1/4 CLK) to place in center of preamble.
1183           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1184           {
1185             delay[bl_i] += QRTR_CLK;
1186             // add "delay[]" values to "final_delay[][]" for rolling average
1187             final_delay[channel_i][bl_i] += delay[bl_i];
1188             // set timing based on rolling average values
1189             set_rcvn(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
1190           } // bl_i loop
1191 #else
1192           // Finally increment delay by 32 PI (1/4 CLK) to place in center of preamble.
1193           for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
1194           {
1195             delay[bl_i] += QRTR_CLK;
1196             set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
1197           } // bl_i loop
1198 
1199 #endif // R2R_SHARING
1200 
1201           // disable FIFORST
1202           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i += 2)
1203           {
1204             isbM32m(DDRPHY, (B01PTRCTL1 + ((bl_i >> 1) * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), BIT8,
1205                 BIT8); // 1 is disabled
1206           } // bl_i loop
1207 
1208 #endif // BACKUP_RCVN
1209 
1210         } // if rank is enabled
1211       } // rank_i loop
1212     } // if channel is enabled
1213   } // channel_i loop
1214 
1215 #ifndef BACKUP_RCVN
1216   // restore original
1217   isbW32m(MCU, DTR1, dtr1save.raw);
1218 #endif
1219 
1220 #ifdef MRC_SV
1221   if (mrc_params->tune_rcvn)
1222   {
1223     uint32_t rcven, val;
1224     uint32_t rdcmd2rcven;
1225 
1226     /*
1227      Formulas for RDCMD2DATAVALID & DIFFAMP dynamic timings
1228 
1229      1. Set after RCVEN training
1230 
1231      //Tune RDCMD2DATAVALID
1232 
1233      x80/x84[21:16]
1234      MAX OF 2 RANKS : round up (rdcmd2rcven (rcven 1x) + 2x x 2 + PI/128) + 5
1235 
1236      //rdcmd2rcven x80/84[12:8]
1237      //rcven 2x x70[23:20] & [11:8]
1238 
1239      //Tune DIFFAMP Timings
1240 
1241      //diffampen launch x88[20:16] & [4:0]  -- B01LATCTL1
1242      MIN OF 2 RANKS : round down (rcven 1x + 2x x 2 + PI/128) - 1
1243 
1244      //diffampen length x8C/x90 [13:8]   -- B0ONDURCTL B1ONDURCTL
1245      MAX OF 2 RANKS : roundup (rcven 1x + 2x x 2 + PI/128) + 5
1246 
1247 
1248      2. need to do a fiforst after settings these values
1249     */
1250 
1251     DPF(D_INFO, "BEFORE\n");
1252     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0LATCTL0));
1253     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B01LATCTL1));
1254     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0ONDURCTL));
1255 
1256     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1LATCTL0));
1257     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1ONDURCTL));
1258 
1259     rcven = get_rcvn(0, 0, 0) / 128;
1260     rdcmd2rcven = (isbR32m(DDRPHY, B0LATCTL0) >> 8) & 0x1F;
1261     val = rdcmd2rcven + rcven + 6;
1262     isbM32m(DDRPHY, B0LATCTL0, val << 16, (BIT21|BIT20|BIT19|BIT18|BIT17|BIT16));
1263 
1264     val = rdcmd2rcven + rcven - 1;
1265     isbM32m(DDRPHY, B01LATCTL1, val << 0, (BIT4|BIT3|BIT2|BIT1|BIT0));
1266 
1267     val = rdcmd2rcven + rcven + 5;
1268     isbM32m(DDRPHY, B0ONDURCTL, val << 8, (BIT13|BIT12|BIT11|BIT10|BIT9|BIT8));
1269 
1270     rcven = get_rcvn(0, 0, 1) / 128;
1271     rdcmd2rcven = (isbR32m(DDRPHY, B1LATCTL0) >> 8) & 0x1F;
1272     val = rdcmd2rcven + rcven + 6;
1273     isbM32m(DDRPHY, B1LATCTL0, val << 16, (BIT21|BIT20|BIT19|BIT18|BIT17|BIT16));
1274 
1275     val = rdcmd2rcven + rcven - 1;
1276     isbM32m(DDRPHY, B01LATCTL1, val << 16, (BIT20|BIT19|BIT18|BIT17|BIT16));
1277 
1278     val = rdcmd2rcven + rcven + 5;
1279     isbM32m(DDRPHY, B1ONDURCTL, val << 8, (BIT13|BIT12|BIT11|BIT10|BIT9|BIT8));
1280 
1281     DPF(D_INFO, "AFTER\n");
1282     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0LATCTL0));
1283     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B01LATCTL1));
1284     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0ONDURCTL));
1285 
1286     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1LATCTL0));
1287     DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1ONDURCTL));
1288 
1289     DPF(D_INFO, "\nPress a key\n");
1290     mgetc();
1291 
1292     // fifo reset
1293     isbM32m(DDRPHY, B01PTRCTL1, 0, BIT8); // 0 is enabled
1294     delay_n(3);
1295     isbM32m(DDRPHY, B01PTRCTL1, BIT8, BIT8); // 1 is disabled
1296   }
1297 #endif
1298 
1299   LEAVEFN();
1300   return;
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.
check_bls_ex(MRCParams_t * mrc_params,uint32_t address)1306 static uint32_t check_bls_ex(
1307     MRCParams_t *mrc_params,
1308     uint32_t address)
1309 {
1310   uint32_t result;
1311   uint8_t first_run = 0;
1312 
1313   if (mrc_params->hte_setup)
1314   {
1315     mrc_params->hte_setup = 0;
1316 
1317     first_run = 1;
1318     select_hte(mrc_params);
1319   }
1320 
1321   result = WriteStressBitLanesHTE(mrc_params, address, first_run);
1322 
1323   DPF(D_TRN, "check_bls_ex result is %x\n", result);
1324   return result;
1325 }
1326 
1327 // Check memory executing simple write/read/verify at
1328 // the specified address. Bits in the result indicate failure
1329 // on specific byte lane.
check_rw_coarse(MRCParams_t * mrc_params,uint32_t address)1330 static uint32_t check_rw_coarse(
1331     MRCParams_t *mrc_params,
1332     uint32_t address)
1333 {
1334   uint32_t result = 0;
1335   uint8_t first_run = 0;
1336 
1337   if (mrc_params->hte_setup)
1338   {
1339     mrc_params->hte_setup = 0;
1340 
1341     first_run = 1;
1342     select_hte(mrc_params);
1343   }
1344 
1345   result = BasicWriteReadHTE(mrc_params, address, first_run, WRITE_TRAIN);
1346 
1347   DPF(D_TRN, "check_rw_coarse result is %x\n", result);
1348   return result;
1349 }
1350 
1351 // wr_level:
1352 // POST_CODE[major] == 0x06
1353 //
1354 // This function will perform the Write Levelling algorithm (align WCLK and WDQS).
1355 // This algorithm will act on each rank in each channel separately.
wr_level(MRCParams_t * mrc_params)1356 static void wr_level(
1357     MRCParams_t *mrc_params)
1358 {
1359   uint8_t channel_i; // channel counter
1360   uint8_t rank_i; // rank counter
1361   uint8_t bl_i; // byte lane counter
1362   uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
1363 
1364 #ifdef R2R_SHARING
1365   uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
1366 #ifndef BACKUP_WDQS
1367   uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
1368 #endif // BACKUP_WDQS
1369 #endif // R2R_SHARING
1370 
1371 #ifdef BACKUP_WDQS
1372 #else
1373   bool all_edges_found; // determines stop condition for CRS_WR_LVL
1374   uint32_t delay[NUM_BYTE_LANES]; // absolute PI value to be programmed on the byte lane
1375   // static makes it so the data is loaded in the heap once by shadow(), where
1376   // non-static copies the data onto the stack every time this function is called.
1377 
1378   uint32_t address; // address to be checked during COARSE_WR_LVL
1379   RegDTR4 dtr4;
1380   RegDTR4 dtr4save;
1381 #endif // BACKUP_WDQS
1382 
1383   ENTERFN();
1384 
1385   // wr_level starts
1386   post_code(0x06, 0x00);
1387 
1388 #ifdef R2R_SHARING
1389   // need to set "final_delay[][]" elements to "0"
1390   memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
1391 #endif // R2R_SHARING
1392   // loop through each enabled channel
1393   for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
1394   {
1395     if (mrc_params->channel_enables & (1 << channel_i))
1396     {
1397       // perform WRITE LEVELING algorithm on a per rank basis
1398       for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
1399       {
1400         if (mrc_params->rank_enables & (1 << rank_i))
1401         {
1402           // POST_CODE here indicates the current rank and channel being calibrated
1403           post_code(0x06, (0x10 + ((channel_i << 4) | rank_i)));
1404 
1405 #ifdef BACKUP_WDQS
1406           for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
1407           {
1408             set_wdqs(channel_i, rank_i, bl_i, ddr_wdqs[PLATFORM_ID]);
1409             set_wdq(channel_i, rank_i, bl_i, (ddr_wdqs[PLATFORM_ID] - QRTR_CLK));
1410           }
1411 #else
1412 
1413           { // Begin product specific code
1414 
1415             // perform a single PRECHARGE_ALL command to make DRAM state machine go to IDLE state
1416             dram_init_command(DCMD_PREA(rank_i));
1417 
1418             // enable Write Levelling Mode (EMRS1 w/ Write Levelling Mode Enable)
1419             dram_init_command(DCMD_MRS1(rank_i,0x0082));
1420 
1421             // set ODT DRAM Full Time Termination disable in MCU
1422             dtr4.raw = dtr4save.raw = isbR32m(MCU, DTR4);
1423             dtr4.field.ODTDIS = 1;
1424             isbW32m(MCU, DTR4, dtr4.raw);
1425 
1426             for (bl_i = 0; bl_i < ((NUM_BYTE_LANES / bl_divisor) / 2); bl_i++)
1427             {
1428               isbM32m(DDRPHY, DQCTL + (DDRIODQ_BL_OFFSET * bl_i) + (DDRIODQ_CH_OFFSET * channel_i),
1429                   (BIT28 | (0x1 << 8) | (0x1 << 6) | (0x1 << 4) | (0x1 << 2)),
1430                   (BIT28 | (BIT9|BIT8) | (BIT7|BIT6) | (BIT5|BIT4) | (BIT3|BIT2))); // Enable Sandy Bridge Mode (WDQ Tri-State) & Ensure 5 WDQS pulses during Write Leveling
1431             }
1432 
1433             isbM32m(DDRPHY, CCDDR3RESETCTL + (DDRIOCCC_CH_OFFSET * channel_i), (BIT16), (BIT16)); // Write Leveling Mode enabled in IO
1434           } // End product specific code
1435           // Initialise the starting delay to WCLK
1436           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1437           {
1438             { // Begin product specific code
1439               // CLK0 --> RK0
1440               // CLK1 --> RK1
1441               delay[bl_i] = get_wclk(channel_i, rank_i);
1442             } // End product specific code
1443             set_wdqs(channel_i, rank_i, bl_i, delay[bl_i]);
1444           } // bl_i loop
1445           // now find the rising edge
1446           find_rising_edge(mrc_params, delay, channel_i, rank_i, false);
1447           { // Begin product specific code
1448             // disable Write Levelling Mode
1449             isbM32m(DDRPHY, CCDDR3RESETCTL + (DDRIOCCC_CH_OFFSET * channel_i), (0), (BIT16)); // Write Leveling Mode disabled in IO
1450 
1451             for (bl_i = 0; bl_i < ((NUM_BYTE_LANES / bl_divisor) / 2); bl_i++)
1452             {
1453               isbM32m(DDRPHY, DQCTL + (DDRIODQ_BL_OFFSET * bl_i) + (DDRIODQ_CH_OFFSET * channel_i),
1454                   ((0x1 << 8) | (0x1 << 6) | (0x1 << 4) | (0x1 << 2)),
1455                   (BIT28 | (BIT9|BIT8) | (BIT7|BIT6) | (BIT5|BIT4) | (BIT3|BIT2))); // Disable Sandy Bridge Mode & Ensure 4 WDQS pulses during normal operation
1456             } // bl_i loop
1457 
1458             // restore original DTR4
1459             isbW32m(MCU, DTR4, dtr4save.raw);
1460 
1461             // restore original value (Write Levelling Mode Disable)
1462             dram_init_command(DCMD_MRS1(rank_i, mrc_params->mrs1));
1463 
1464             // perform a single PRECHARGE_ALL command to make DRAM state machine go to IDLE state
1465             dram_init_command(DCMD_PREA(rank_i));
1466           } // End product specific code
1467 
1468           post_code(0x06, (0x30 + ((channel_i << 4) | rank_i)));
1469 
1470           // COARSE WRITE LEVEL:
1471           // check that we're on the correct clock edge
1472 
1473           // hte reconfiguration request
1474           mrc_params->hte_setup = 1;
1475 
1476           // start CRS_WR_LVL with WDQS = WDQS + 128 PI
1477           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1478           {
1479             delay[bl_i] = get_wdqs(channel_i, rank_i, bl_i) + FULL_CLK;
1480             set_wdqs(channel_i, rank_i, bl_i, delay[bl_i]);
1481             // program WDQ timings based on WDQS (WDQ = WDQS - 32 PI)
1482             set_wdq(channel_i, rank_i, bl_i, (delay[bl_i] - QRTR_CLK));
1483           } // bl_i loop
1484 
1485           // get an address in the targeted channel/rank
1486           address = get_addr(mrc_params, channel_i, rank_i);
1487           do
1488           {
1489             uint32_t coarse_result = 0x00;
1490             uint32_t coarse_result_mask = byte_lane_mask(mrc_params);
1491             all_edges_found = true; // assume pass
1492 
1493 #ifdef SIM
1494             // need restore memory to idle state as write can be in bad sync
1495             dram_init_command (DCMD_PREA(rank_i));
1496 #endif
1497 
1498             mrc_params->hte_setup = 1;
1499             coarse_result = check_rw_coarse(mrc_params, address);
1500 
1501             // check for failures and margin the byte lane back 128 PI (1 CLK)
1502             for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1503             {
1504               if (coarse_result & (coarse_result_mask << bl_i))
1505               {
1506                 all_edges_found = false;
1507                 delay[bl_i] -= FULL_CLK;
1508                 set_wdqs(channel_i, rank_i, bl_i, delay[bl_i]);
1509                 // program WDQ timings based on WDQS (WDQ = WDQS - 32 PI)
1510                 set_wdq(channel_i, rank_i, bl_i, (delay[bl_i] - QRTR_CLK));
1511               }
1512             } // bl_i loop
1513 
1514           } while (!all_edges_found);
1515 
1516 #ifdef R2R_SHARING
1517           // increment "num_ranks_enabled"
1518           num_ranks_enabled++;
1519           // accumulate "final_delay[][]" values from "delay[]" values for rolling average
1520           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1521           {
1522             final_delay[channel_i][bl_i] += delay[bl_i];
1523             set_wdqs(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
1524             // program WDQ timings based on WDQS (WDQ = WDQS - 32 PI)
1525             set_wdq(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled) - QRTR_CLK);
1526           } // bl_i loop
1527 #endif // R2R_SHARING
1528 #endif // BACKUP_WDQS
1529 
1530         } // if rank is enabled
1531       } // rank_i loop
1532     } // if channel is enabled
1533   } // channel_i loop
1534 
1535   LEAVEFN();
1536   return;
1537 }
1538 
1539 // rd_train:
1540 // POST_CODE[major] == 0x07
1541 //
1542 // This function will perform the READ TRAINING Algorithm on all channels/ranks/byte_lanes simultaneously to minimize execution time.
1543 // The idea here is to train the VREF and RDQS (and eventually RDQ) values to achieve maximum READ margins.
1544 // The algorithm will first determine the X coordinate (RDQS setting).
1545 // This is done by collapsing the VREF eye until we find a minimum required RDQS eye for VREF_MIN and VREF_MAX.
1546 // Then we take the averages of the RDQS eye at VREF_MIN and VREF_MAX, then average those; this will be the final X coordinate.
1547 // The algorithm will then determine the Y coordinate (VREF setting).
1548 // This is done by collapsing the RDQS eye until we find a minimum required VREF eye for RDQS_MIN and RDQS_MAX.
1549 // Then we take the averages of the VREF eye at RDQS_MIN and RDQS_MAX, then average those; this will be the final Y coordinate.
1550 // NOTE: this algorithm assumes the eye curves have a one-to-one relationship, meaning for each X the curve has only one Y and vice-a-versa.
rd_train(MRCParams_t * mrc_params)1551 static void rd_train(
1552     MRCParams_t *mrc_params)
1553 {
1554 
1555 #define MIN_RDQS_EYE 10 // in PI Codes
1556 #define MIN_VREF_EYE 10 // in VREF Codes
1557 #define RDQS_STEP 1     // how many RDQS codes to jump while margining
1558 #define VREF_STEP 1     // how many VREF codes to jump while margining
1559 #define VREF_MIN (0x00) // offset into "vref_codes[]" for minimum allowed VREF setting
1560 #define VREF_MAX (0x3F) // offset into "vref_codes[]" for maximum allowed VREF setting
1561 #define RDQS_MIN (0x00) // minimum RDQS delay value
1562 #define RDQS_MAX (0x3F) // maximum RDQS delay value
1563 #define B 0 // BOTTOM VREF
1564 #define T 1 // TOP VREF
1565 #define L 0 // LEFT RDQS
1566 #define R 1 // RIGHT RDQS
1567 
1568   uint8_t channel_i; // channel counter
1569   uint8_t rank_i; // rank counter
1570   uint8_t bl_i; // byte lane counter
1571   uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
1572 #ifdef BACKUP_RDQS
1573 #else
1574   uint8_t side_x; // tracks LEFT/RIGHT approach vectors
1575   uint8_t side_y; // tracks BOTTOM/TOP approach vectors
1576   uint8_t x_coordinate[2/*side_x*/][2/*side_y*/][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; // X coordinate data (passing RDQS values) for approach vectors
1577   uint8_t y_coordinate[2/*side_x*/][2/*side_y*/][NUM_CHANNELS][NUM_BYTE_LANES]; // Y coordinate data (passing VREF values) for approach vectors
1578   uint8_t x_center[NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; // centered X (RDQS)
1579   uint8_t y_center[NUM_CHANNELS][NUM_BYTE_LANES]; // centered Y (VREF)
1580   uint32_t address; // target address for "check_bls_ex()"
1581   uint32_t result; // result of "check_bls_ex()"
1582   uint32_t bl_mask; // byte lane mask for "result" checking
1583 #ifdef R2R_SHARING
1584   uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
1585   uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
1586 #endif // R2R_SHARING
1587 #endif // BACKUP_RDQS
1588   // rd_train starts
1589   post_code(0x07, 0x00);
1590 
1591   ENTERFN();
1592 
1593 #ifdef BACKUP_RDQS
1594   for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++)
1595   {
1596     if (mrc_params->channel_enables & (1<<channel_i))
1597     {
1598       for (rank_i=0; rank_i<NUM_RANKS; rank_i++)
1599       {
1600         if (mrc_params->rank_enables & (1<<rank_i))
1601         {
1602           for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
1603           {
1604             set_rdqs(channel_i, rank_i, bl_i, ddr_rdqs[PLATFORM_ID]);
1605           } // bl_i loop
1606         } // if rank is enabled
1607       } // rank_i loop
1608     } // if channel is enabled
1609   } // channel_i loop
1610 #else
1611   // initialise x/y_coordinate arrays
1612   for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
1613   {
1614     if (mrc_params->channel_enables & (1 << channel_i))
1615     {
1616       for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
1617       {
1618         if (mrc_params->rank_enables & (1 << rank_i))
1619         {
1620           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1621           {
1622             // x_coordinate:
1623             x_coordinate[L][B][channel_i][rank_i][bl_i] = RDQS_MIN;
1624             x_coordinate[R][B][channel_i][rank_i][bl_i] = RDQS_MAX;
1625             x_coordinate[L][T][channel_i][rank_i][bl_i] = RDQS_MIN;
1626             x_coordinate[R][T][channel_i][rank_i][bl_i] = RDQS_MAX;
1627             // y_coordinate:
1628             y_coordinate[L][B][channel_i][bl_i] = VREF_MIN;
1629             y_coordinate[R][B][channel_i][bl_i] = VREF_MIN;
1630             y_coordinate[L][T][channel_i][bl_i] = VREF_MAX;
1631             y_coordinate[R][T][channel_i][bl_i] = VREF_MAX;
1632           } // bl_i loop
1633         } // if rank is enabled
1634       } // rank_i loop
1635     } // if channel is enabled
1636   } // channel_i loop
1637 
1638   // initialise other variables
1639   bl_mask = byte_lane_mask(mrc_params);
1640   address = get_addr(mrc_params, 0, 0);
1641 
1642 #ifdef R2R_SHARING
1643   // need to set "final_delay[][]" elements to "0"
1644   memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
1645 #endif // R2R_SHARING
1646 
1647   // look for passing coordinates
1648   for (side_y = B; side_y <= T; side_y++)
1649   {
1650     for (side_x = L; side_x <= R; side_x++)
1651     {
1652 
1653       post_code(0x07, (0x10 + (side_y * 2) + (side_x)));
1654 
1655       // find passing values
1656       for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
1657       {
1658         if (mrc_params->channel_enables & (0x1 << channel_i))
1659         {
1660           for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
1661           {
1662 
1663             if (mrc_params->rank_enables & (0x1 << rank_i))
1664             {
1665               // set x/y_coordinate search starting settings
1666               for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1667               {
1668                 set_rdqs(channel_i, rank_i, bl_i, x_coordinate[side_x][side_y][channel_i][rank_i][bl_i]);
1669                 set_vref(channel_i, bl_i, y_coordinate[side_x][side_y][channel_i][bl_i]);
1670               } // bl_i loop
1671               // get an address in the target channel/rank
1672               address = get_addr(mrc_params, channel_i, rank_i);
1673 
1674               // request HTE reconfiguration
1675               mrc_params->hte_setup = 1;
1676 
1677               // test the settings
1678               do
1679               {
1680 
1681                 // result[07:00] == failing byte lane (MAX 8)
1682                 result = check_bls_ex( mrc_params, address);
1683 
1684                 // check for failures
1685                 if (result & 0xFF)
1686                 {
1687                   // at least 1 byte lane failed
1688                   for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1689                   {
1690                     if (result & (bl_mask << bl_i))
1691                     {
1692                       // adjust the RDQS values accordingly
1693                       if (side_x == L)
1694                       {
1695                         x_coordinate[L][side_y][channel_i][rank_i][bl_i] += RDQS_STEP;
1696                       }
1697                       else
1698                       {
1699                         x_coordinate[R][side_y][channel_i][rank_i][bl_i] -= RDQS_STEP;
1700                       }
1701                       // check that we haven't closed the RDQS_EYE too much
1702                       if ((x_coordinate[L][side_y][channel_i][rank_i][bl_i] > (RDQS_MAX - MIN_RDQS_EYE)) ||
1703                           (x_coordinate[R][side_y][channel_i][rank_i][bl_i] < (RDQS_MIN + MIN_RDQS_EYE))
1704                           ||
1705                           (x_coordinate[L][side_y][channel_i][rank_i][bl_i]
1706                               == x_coordinate[R][side_y][channel_i][rank_i][bl_i]))
1707                       {
1708                         // not enough RDQS margin available at this VREF
1709                         // update VREF values accordingly
1710                         if (side_y == B)
1711                         {
1712                           y_coordinate[side_x][B][channel_i][bl_i] += VREF_STEP;
1713                         }
1714                         else
1715                         {
1716                           y_coordinate[side_x][T][channel_i][bl_i] -= VREF_STEP;
1717                         }
1718                         // check that we haven't closed the VREF_EYE too much
1719                         if ((y_coordinate[side_x][B][channel_i][bl_i] > (VREF_MAX - MIN_VREF_EYE)) ||
1720                             (y_coordinate[side_x][T][channel_i][bl_i] < (VREF_MIN + MIN_VREF_EYE)) ||
1721                             (y_coordinate[side_x][B][channel_i][bl_i] == y_coordinate[side_x][T][channel_i][bl_i]))
1722                         {
1723                           // VREF_EYE collapsed below MIN_VREF_EYE
1724                           training_message(channel_i, rank_i, bl_i);
1725                           post_code(0xEE, (0x70 + (side_y * 2) + (side_x)));
1726                         }
1727                         else
1728                         {
1729                           // update the VREF setting
1730                           set_vref(channel_i, bl_i, y_coordinate[side_x][side_y][channel_i][bl_i]);
1731                           // reset the X coordinate to begin the search at the new VREF
1732                           x_coordinate[side_x][side_y][channel_i][rank_i][bl_i] =
1733                               (side_x == L) ? (RDQS_MIN) : (RDQS_MAX);
1734                         }
1735                       }
1736                       // update the RDQS setting
1737                       set_rdqs(channel_i, rank_i, bl_i, x_coordinate[side_x][side_y][channel_i][rank_i][bl_i]);
1738                     } // if bl_i failed
1739                   } // bl_i loop
1740                 } // at least 1 byte lane failed
1741               } while (result & 0xFF);
1742             } // if rank is enabled
1743           } // rank_i loop
1744         } // if channel is enabled
1745       } // channel_i loop
1746     } // side_x loop
1747   } // side_y loop
1748 
1749   post_code(0x07, 0x20);
1750 
1751   // find final RDQS (X coordinate) & final VREF (Y coordinate)
1752   for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
1753   {
1754     if (mrc_params->channel_enables & (1 << channel_i))
1755     {
1756       for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
1757       {
1758         if (mrc_params->rank_enables & (1 << rank_i))
1759         {
1760           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1761           {
1762             uint32_t tempD1;
1763             uint32_t tempD2;
1764 
1765             // x_coordinate:
1766             DPF(D_INFO, "RDQS T/B eye rank%d lane%d : %d-%d %d-%d\n", rank_i, bl_i,
1767                 x_coordinate[L][T][channel_i][rank_i][bl_i],
1768                 x_coordinate[R][T][channel_i][rank_i][bl_i],
1769                 x_coordinate[L][B][channel_i][rank_i][bl_i],
1770                 x_coordinate[R][B][channel_i][rank_i][bl_i]);
1771 
1772             tempD1 = (x_coordinate[R][T][channel_i][rank_i][bl_i] + x_coordinate[L][T][channel_i][rank_i][bl_i]) / 2; // average the TOP side LEFT & RIGHT values
1773             tempD2 = (x_coordinate[R][B][channel_i][rank_i][bl_i] + x_coordinate[L][B][channel_i][rank_i][bl_i]) / 2; // average the BOTTOM side LEFT & RIGHT values
1774             x_center[channel_i][rank_i][bl_i] = (uint8_t) ((tempD1 + tempD2) / 2); // average the above averages
1775 
1776             // y_coordinate:
1777             DPF(D_INFO, "VREF R/L eye lane%d : %d-%d %d-%d\n", bl_i,
1778                 y_coordinate[R][B][channel_i][bl_i],
1779                 y_coordinate[R][T][channel_i][bl_i],
1780                 y_coordinate[L][B][channel_i][bl_i],
1781                 y_coordinate[L][T][channel_i][bl_i]);
1782 
1783             tempD1 = (y_coordinate[R][T][channel_i][bl_i] + y_coordinate[R][B][channel_i][bl_i]) / 2; // average the RIGHT side TOP & BOTTOM values
1784             tempD2 = (y_coordinate[L][T][channel_i][bl_i] + y_coordinate[L][B][channel_i][bl_i]) / 2; // average the LEFT side TOP & BOTTOM values
1785             y_center[channel_i][bl_i] = (uint8_t) ((tempD1 + tempD2) / 2); // average the above averages
1786           } // bl_i loop
1787         } // if rank is enabled
1788       } // rank_i loop
1789     } // if channel is enabled
1790   } // channel_i loop
1791 
1792 #ifdef RX_EYE_CHECK
1793   // perform an eye check
1794   for (side_y=B; side_y<=T; side_y++)
1795   {
1796     for (side_x=L; side_x<=R; side_x++)
1797     {
1798 
1799       post_code(0x07, (0x30 + (side_y * 2) + (side_x)));
1800 
1801       // update the settings for the eye check
1802       for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++)
1803       {
1804         if (mrc_params->channel_enables & (1<<channel_i))
1805         {
1806           for (rank_i=0; rank_i<NUM_RANKS; rank_i++)
1807           {
1808             if (mrc_params->rank_enables & (1<<rank_i))
1809             {
1810               for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
1811               {
1812                 if (side_x == L)
1813                 {
1814                   set_rdqs(channel_i, rank_i, bl_i, (x_center[channel_i][rank_i][bl_i] - (MIN_RDQS_EYE / 2)));
1815                 }
1816                 else
1817                 {
1818                   set_rdqs(channel_i, rank_i, bl_i, (x_center[channel_i][rank_i][bl_i] + (MIN_RDQS_EYE / 2)));
1819                 }
1820                 if (side_y == B)
1821                 {
1822                   set_vref(channel_i, bl_i, (y_center[channel_i][bl_i] - (MIN_VREF_EYE / 2)));
1823                 }
1824                 else
1825                 {
1826                   set_vref(channel_i, bl_i, (y_center[channel_i][bl_i] + (MIN_VREF_EYE / 2)));
1827                 }
1828               } // bl_i loop
1829             } // if rank is enabled
1830           } // rank_i loop
1831         } // if channel is enabled
1832       } // channel_i loop
1833 
1834       // request HTE reconfiguration
1835       mrc_params->hte_setup = 1;
1836 
1837       // check the eye
1838       if (check_bls_ex( mrc_params, address) & 0xFF)
1839       {
1840         // one or more byte lanes failed
1841         post_code(0xEE, (0x74 + (side_x * 2) + (side_y)));
1842       }
1843     } // side_x loop
1844   } // side_y loop
1845 #endif // RX_EYE_CHECK
1846 
1847   post_code(0x07, 0x40);
1848 
1849   // set final placements
1850   for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
1851   {
1852     if (mrc_params->channel_enables & (1 << channel_i))
1853     {
1854       for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
1855       {
1856         if (mrc_params->rank_enables & (1 << rank_i))
1857         {
1858 #ifdef R2R_SHARING
1859           // increment "num_ranks_enabled"
1860           num_ranks_enabled++;
1861 #endif // R2R_SHARING
1862           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1863           {
1864             // x_coordinate:
1865 #ifdef R2R_SHARING
1866             final_delay[channel_i][bl_i] += x_center[channel_i][rank_i][bl_i];
1867             set_rdqs(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
1868 #else
1869             set_rdqs(channel_i, rank_i, bl_i, x_center[channel_i][rank_i][bl_i]);
1870 #endif // R2R_SHARING
1871             // y_coordinate:
1872             set_vref(channel_i, bl_i, y_center[channel_i][bl_i]);
1873           } // bl_i loop
1874         } // if rank is enabled
1875       } // rank_i loop
1876     } // if channel is enabled
1877   } // channel_i loop
1878 #endif // BACKUP_RDQS
1879   LEAVEFN();
1880   return;
1881 }
1882 
1883 // wr_train:
1884 // POST_CODE[major] == 0x08
1885 //
1886 // This function will perform the WRITE TRAINING Algorithm on all channels/ranks/byte_lanes simultaneously to minimize execution time.
1887 // The idea here is to train the WDQ timings to achieve maximum WRITE margins.
1888 // The algorithm will start with WDQ at the current WDQ setting (tracks WDQS in WR_LVL) +/- 32 PIs (+/- 1/4 CLK) and collapse the eye until all data patterns pass.
1889 // This is because WDQS will be aligned to WCLK by the Write Leveling algorithm and WDQ will only ever have a 1/2 CLK window of validity.
wr_train(MRCParams_t * mrc_params)1890 static void wr_train(
1891     MRCParams_t *mrc_params)
1892 {
1893 
1894 #define WDQ_STEP 1 // how many WDQ codes to jump while margining
1895 #define L 0 // LEFT side loop value definition
1896 #define R 1 // RIGHT side loop value definition
1897 
1898   uint8_t channel_i; // channel counter
1899   uint8_t rank_i; // rank counter
1900   uint8_t bl_i; // byte lane counter
1901   uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
1902 #ifdef BACKUP_WDQ
1903 #else
1904   uint8_t side_i; // LEFT/RIGHT side indicator (0=L, 1=R)
1905   uint32_t tempD; // temporary DWORD
1906   uint32_t delay[2/*side_i*/][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; // 2 arrays, for L & R side passing delays
1907   uint32_t address; // target address for "check_bls_ex()"
1908   uint32_t result; // result of "check_bls_ex()"
1909   uint32_t bl_mask; // byte lane mask for "result" checking
1910 #ifdef R2R_SHARING
1911   uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
1912   uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
1913 #endif // R2R_SHARING
1914 #endif // BACKUP_WDQ
1915 
1916   // wr_train starts
1917   post_code(0x08, 0x00);
1918 
1919   ENTERFN();
1920 
1921 #ifdef BACKUP_WDQ
1922   for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++)
1923   {
1924     if (mrc_params->channel_enables & (1<<channel_i))
1925     {
1926       for (rank_i=0; rank_i<NUM_RANKS; rank_i++)
1927       {
1928         if (mrc_params->rank_enables & (1<<rank_i))
1929         {
1930           for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
1931           {
1932             set_wdq(channel_i, rank_i, bl_i, ddr_wdq[PLATFORM_ID]);
1933           } // bl_i loop
1934         } // if rank is enabled
1935       } // rank_i loop
1936     } // if channel is enabled
1937   } // channel_i loop
1938 #else
1939   // initialise "delay"
1940   for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
1941   {
1942     if (mrc_params->channel_enables & (1 << channel_i))
1943     {
1944       for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
1945       {
1946         if (mrc_params->rank_enables & (1 << rank_i))
1947         {
1948           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1949           {
1950             // want to start with WDQ = (WDQS - QRTR_CLK) +/- QRTR_CLK
1951             tempD = get_wdqs(channel_i, rank_i, bl_i) - QRTR_CLK;
1952             delay[L][channel_i][rank_i][bl_i] = tempD - QRTR_CLK;
1953             delay[R][channel_i][rank_i][bl_i] = tempD + QRTR_CLK;
1954           } // bl_i loop
1955         } // if rank is enabled
1956       } // rank_i loop
1957     } // if channel is enabled
1958   } // channel_i loop
1959 
1960   // initialise other variables
1961   bl_mask = byte_lane_mask(mrc_params);
1962   address = get_addr(mrc_params, 0, 0);
1963 
1964 #ifdef R2R_SHARING
1965   // need to set "final_delay[][]" elements to "0"
1966   memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
1967 #endif // R2R_SHARING
1968 
1969   // start algorithm on the LEFT side and train each channel/bl until no failures are observed, then repeat for the RIGHT side.
1970   for (side_i = L; side_i <= R; side_i++)
1971   {
1972     post_code(0x08, (0x10 + (side_i)));
1973 
1974     // set starting values
1975     for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
1976     {
1977       if (mrc_params->channel_enables & (1 << channel_i))
1978       {
1979         for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
1980         {
1981           if (mrc_params->rank_enables & (1 << rank_i))
1982           {
1983             for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
1984             {
1985               set_wdq(channel_i, rank_i, bl_i, delay[side_i][channel_i][rank_i][bl_i]);
1986             } // bl_i loop
1987           } // if rank is enabled
1988         } // rank_i loop
1989       } // if channel is enabled
1990     } // channel_i loop
1991 
1992     // find passing values
1993     for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
1994     {
1995       if (mrc_params->channel_enables & (0x1 << channel_i))
1996       {
1997         for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
1998         {
1999           if (mrc_params->rank_enables & (0x1 << rank_i))
2000           {
2001             // get an address in the target channel/rank
2002             address = get_addr(mrc_params, channel_i, rank_i);
2003 
2004             // request HTE reconfiguration
2005             mrc_params->hte_setup = 1;
2006 
2007             // check the settings
2008             do
2009             {
2010 
2011 #ifdef SIM
2012               // need restore memory to idle state as write can be in bad sync
2013               dram_init_command (DCMD_PREA(rank_i));
2014 #endif
2015 
2016               // result[07:00] == failing byte lane (MAX 8)
2017               result = check_bls_ex( mrc_params, address);
2018               // check for failures
2019               if (result & 0xFF)
2020               {
2021                 // at least 1 byte lane failed
2022                 for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
2023                 {
2024                   if (result & (bl_mask << bl_i))
2025                   {
2026                     if (side_i == L)
2027                     {
2028                       delay[L][channel_i][rank_i][bl_i] += WDQ_STEP;
2029                     }
2030                     else
2031                     {
2032                       delay[R][channel_i][rank_i][bl_i] -= WDQ_STEP;
2033                     }
2034                     // check for algorithm failure
2035                     if (delay[L][channel_i][rank_i][bl_i] != delay[R][channel_i][rank_i][bl_i])
2036                     {
2037                       // margin available, update delay setting
2038                       set_wdq(channel_i, rank_i, bl_i, delay[side_i][channel_i][rank_i][bl_i]);
2039                     }
2040                     else
2041                     {
2042                       // no margin available, notify the user and halt
2043                       training_message(channel_i, rank_i, bl_i);
2044                       post_code(0xEE, (0x80 + side_i));
2045                     }
2046                   } // if bl_i failed
2047                 } // bl_i loop
2048               } // at least 1 byte lane failed
2049             } while (result & 0xFF); // stop when all byte lanes pass
2050           } // if rank is enabled
2051         } // rank_i loop
2052       } // if channel is enabled
2053     } // channel_i loop
2054   } // side_i loop
2055 
2056   // program WDQ to the middle of passing window
2057   for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
2058   {
2059     if (mrc_params->channel_enables & (1 << channel_i))
2060     {
2061       for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
2062       {
2063         if (mrc_params->rank_enables & (1 << rank_i))
2064         {
2065 #ifdef R2R_SHARING
2066           // increment "num_ranks_enabled"
2067           num_ranks_enabled++;
2068 #endif // R2R_SHARING
2069           for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
2070           {
2071 
2072             DPF(D_INFO, "WDQ eye rank%d lane%d : %d-%d\n", rank_i, bl_i,
2073                 delay[L][channel_i][rank_i][bl_i],
2074                 delay[R][channel_i][rank_i][bl_i]);
2075 
2076             tempD = (delay[R][channel_i][rank_i][bl_i] + delay[L][channel_i][rank_i][bl_i]) / 2;
2077 
2078 #ifdef R2R_SHARING
2079             final_delay[channel_i][bl_i] += tempD;
2080             set_wdq(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
2081 #else
2082             set_wdq(channel_i, rank_i, bl_i, tempD);
2083 #endif // R2R_SHARING
2084 
2085           } // bl_i loop
2086         } // if rank is enabled
2087       } // rank_i loop
2088     } // if channel is enabled
2089   } // channel_i loop
2090 #endif // BACKUP_WDQ
2091   LEAVEFN();
2092   return;
2093 }
2094 
2095 // Wrapper for jedec initialisation routine
perform_jedec_init(MRCParams_t * mrc_params)2096 static void perform_jedec_init(
2097     MRCParams_t *mrc_params)
2098 {
2099   jedec_init(mrc_params, 0);
2100 }
2101 
2102 // Configure DDRPHY for Auto-Refresh, Periodic Compensations,
2103 // Dynamic Diff-Amp, ZQSPERIOD, Auto-Precharge, CKE Power-Down
set_auto_refresh(MRCParams_t * mrc_params)2104 static void set_auto_refresh(
2105     MRCParams_t *mrc_params)
2106 {
2107   uint32_t channel_i;
2108   uint32_t rank_i;
2109   uint32_t bl_i;
2110   uint32_t bl_divisor = /*(mrc_params->channel_width==x16)?2:*/1;
2111   uint32_t tempD;
2112 
2113   ENTERFN();
2114 
2115   // enable Auto-Refresh, Periodic Compensations, Dynamic Diff-Amp, ZQSPERIOD, Auto-Precharge, CKE Power-Down
2116   for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
2117   {
2118     if (mrc_params->channel_enables & (1 << channel_i))
2119     {
2120       // Enable Periodic RCOMPS
2121       isbM32m(DDRPHY, CMPCTRL, (BIT1), (BIT1));
2122 
2123 
2124       // Enable Dynamic DiffAmp & Set Read ODT Value
2125       switch (mrc_params->rd_odt_value)
2126       {
2127         case 0: tempD = 0x3F; break;  // OFF
2128         default: tempD = 0x00; break; // Auto
2129       } // rd_odt_value switch
2130 
2131       for (bl_i=0; bl_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_i++)
2132       {
2133         isbM32m(DDRPHY, (B0OVRCTL + (bl_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)),
2134             ((0x00<<16)|(tempD<<10)),
2135             ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10))); // Override: DIFFAMP, ODT
2136 
2137         isbM32m(DDRPHY, (B1OVRCTL + (bl_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)),
2138             ((0x00<<16)|(tempD<<10)),
2139             ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10)));// Override: DIFFAMP, ODT
2140       } // bl_i loop
2141 
2142       // Issue ZQCS command
2143       for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
2144       {
2145         if (mrc_params->rank_enables & (1 << rank_i))
2146         {
2147           dram_init_command(DCMD_ZQCS(rank_i));
2148         } // if rank_i enabled
2149       } // rank_i loop
2150 
2151     } // if channel_i enabled
2152   } // channel_i loop
2153 
2154   clear_pointers();
2155 
2156   LEAVEFN();
2157   return;
2158 }
2159 
2160 // Depending on configuration enables ECC support.
2161 // Available memory size is decresed, and updated with 0s
2162 // in order to clear error status. Address mode 2 forced.
ecc_enable(MRCParams_t * mrc_params)2163 static void ecc_enable(
2164     MRCParams_t *mrc_params)
2165 {
2166   RegDRP Drp;
2167   RegDSCH Dsch;
2168   RegDECCCTRL Ctr;
2169 
2170   if (mrc_params->ecc_enables == 0) return;
2171 
2172   ENTERFN();
2173 
2174   // Configuration required in ECC mode
2175   Drp.raw = isbR32m(MCU, DRP);
2176   Drp.field.addressMap = 2;
2177   Drp.field.split64 = 1;
2178   isbW32m(MCU, DRP, Drp.raw);
2179 
2180   // Disable new request bypass
2181   Dsch.raw = isbR32m(MCU, DSCH);
2182   Dsch.field.NEWBYPDIS = 1;
2183   isbW32m(MCU, DSCH, Dsch.raw);
2184 
2185   // Enable ECC
2186   Ctr.raw = 0;
2187   Ctr.field.SBEEN = 1;
2188   Ctr.field.DBEEN = 1;
2189   Ctr.field.ENCBGEN = 1;
2190   isbW32m(MCU, DECCCTRL, Ctr.raw);
2191 
2192 #ifdef SIM
2193   // Read back to be sure writing took place
2194   Ctr.raw = isbR32m(MCU, DECCCTRL);
2195 #endif
2196 
2197   // Assume 8 bank memory, one bank is gone for ECC
2198   mrc_params->mem_size -= mrc_params->mem_size / 8;
2199 
2200   // For S3 resume memory content has to be preserved
2201   if (mrc_params->boot_mode != bmS3)
2202   {
2203     select_hte(mrc_params);
2204     HteMemInit(mrc_params, MrcMemInit, MrcHaltHteEngineOnError);
2205     select_memory_manager(mrc_params);
2206   }
2207 
2208   LEAVEFN();
2209   return;
2210 }
2211 
2212 // Lock MCU registers at the end of initialisation sequence.
lock_registers(MRCParams_t * mrc_params)2213 static void lock_registers(
2214     MRCParams_t *mrc_params)
2215 {
2216   RegDCO Dco;
2217 
2218   ENTERFN();
2219 
2220   Dco.raw = isbR32m(MCU, DCO);
2221   Dco.field.PMIDIS = 0;          //0 - PRI enabled
2222   Dco.field.PMICTL = 0;          //0 - PRI owned by MEMORY_MANAGER
2223   Dco.field.DRPLOCK = 1;
2224   Dco.field.REUTLOCK = 1;
2225   isbW32m(MCU, DCO, Dco.raw);
2226 
2227   LEAVEFN();
2228 
2229 }
2230 
2231 #ifdef MRC_SV
2232 
2233 // cache write back invalidate
asm_wbinvd(void)2234 static void asm_wbinvd(void)
2235 {
2236 #if defined (SIM) || defined (GCC)
2237   asm(
2238     "wbinvd;"
2239   );
2240 #else
2241   __asm wbinvd;
2242 #endif
2243 }
2244 
2245 // cache invalidate
asm_invd(void)2246 static void asm_invd(void)
2247 {
2248 #if defined (SIM) || defined (GCC)
2249   asm(
2250       "invd;"
2251   );
2252 #else
2253   __asm invd;
2254 #endif
2255 }
2256 
2257 
cpu_read(void)2258 static void cpu_read(void)
2259 {
2260   uint32_t adr, dat, limit;
2261 
2262   asm_invd();
2263 
2264   limit = 8 * 1024;
2265   for (adr = 0; adr < limit; adr += 4)
2266   {
2267     dat = *(uint32_t*) adr;
2268     if ((adr & 0x0F) == 0)
2269     {
2270       DPF(D_INFO, "\n%x : ", adr);
2271     }
2272     DPF(D_INFO, "%x ", dat);
2273   }
2274   DPF(D_INFO, "\n");
2275 
2276   DPF(D_INFO, "CPU read done\n");
2277 }
2278 
2279 
cpu_write(void)2280 static void cpu_write(void)
2281 {
2282   uint32_t adr, limit;
2283 
2284   limit = 8 * 1024;
2285   for (adr = 0; adr < limit; adr += 4)
2286   {
2287     *(uint32_t*) adr = 0xDEAD0000 + adr;
2288   }
2289 
2290   asm_wbinvd();
2291 
2292   DPF(D_INFO, "CPU write done\n");
2293 }
2294 
2295 
cpu_memory_test(MRCParams_t * mrc_params)2296 static void cpu_memory_test(
2297     MRCParams_t *mrc_params)
2298 {
2299   uint32_t result = 0;
2300   uint32_t val, dat, adr, adr0, step, limit;
2301   uint64_t my_tsc;
2302 
2303   ENTERFN();
2304 
2305   asm_invd();
2306 
2307   adr0 = 1 * 1024 * 1024;
2308   limit = 256 * 1024 * 1024;
2309 
2310   for (step = 0; step <= 4; step++)
2311   {
2312     DPF(D_INFO, "Mem test step %d starting from %xh\n", step, adr0);
2313 
2314     my_tsc = read_tsc();
2315     for (adr = adr0; adr < limit; adr += sizeof(uint32_t))
2316     {
2317       if (step == 0)      dat = adr;
2318       else if (step == 1) dat = (1 << ((adr >> 2) & 0x1f));
2319       else if (step == 2) dat = ~(1 << ((adr >> 2) & 0x1f));
2320       else if (step == 3) dat = 0x5555AAAA;
2321       else if (step == 4) dat = 0xAAAA5555;
2322 
2323       *(uint32_t*) adr = dat;
2324     }
2325     DPF(D_INFO, "Write time %llXh\n", read_tsc() - my_tsc);
2326 
2327     my_tsc = read_tsc();
2328     for (adr = adr0; adr < limit; adr += sizeof(uint32_t))
2329     {
2330       if (step == 0)      dat = adr;
2331       else if (step == 1) dat = (1 << ((adr >> 2) & 0x1f));
2332       else if (step == 2) dat = ~(1 << ((adr >> 2) & 0x1f));
2333       else if (step == 3) dat = 0x5555AAAA;
2334       else if (step == 4) dat = 0xAAAA5555;
2335 
2336       val = *(uint32_t*) adr;
2337 
2338       if (val != dat)
2339       {
2340         DPF(D_INFO, "%x vs. %x@%x\n", dat, val, adr);
2341         result = adr|BIT31;
2342       }
2343     }
2344     DPF(D_INFO, "Read time %llXh\n", read_tsc() - my_tsc);
2345   }
2346 
2347   DPF( D_INFO, "Memory test result %x\n", result);
2348   LEAVEFN();
2349 }
2350 #endif // MRC_SV
2351 
2352 
2353 // Execute memory test, if error dtected it is
2354 // indicated in mrc_params->status.
memory_test(MRCParams_t * mrc_params)2355 static void memory_test(
2356   MRCParams_t *mrc_params)
2357 {
2358   uint32_t result = 0;
2359 
2360   ENTERFN();
2361 
2362   select_hte(mrc_params);
2363   result = HteMemInit(mrc_params, MrcMemTest, MrcHaltHteEngineOnError);
2364   select_memory_manager(mrc_params);
2365 
2366   DPF(D_INFO, "Memory test result %x\n", result);
2367   mrc_params->status = ((result == 0) ? MRC_SUCCESS : MRC_E_MEMTEST);
2368   LEAVEFN();
2369 }
2370 
2371 
2372 // Force same timings as with backup settings
static_timings(MRCParams_t * mrc_params)2373 static void static_timings(
2374   MRCParams_t *mrc_params)
2375 
2376 {
2377   uint8_t ch, rk, bl;
2378 
2379   for (ch = 0; ch < NUM_CHANNELS; ch++)
2380   {
2381     for (rk = 0; rk < NUM_RANKS; rk++)
2382     {
2383       for (bl = 0; bl < NUM_BYTE_LANES; bl++)
2384       {
2385         set_rcvn(ch, rk, bl, 498);  // RCVN
2386         set_rdqs(ch, rk, bl,  24);  // RDQS
2387         set_wdqs(ch, rk, bl, 292);  // WDQS
2388         set_wdq( ch, rk, bl, 260);  // WDQ
2389         if (rk == 0)
2390         {
2391           set_vref(ch, bl, 32); // VREF (RANK0 only)
2392         }
2393       }
2394       set_wctl(ch, rk, 217); // WCTL
2395     }
2396     set_wcmd(ch, 220); // WCMD
2397   }
2398 
2399   return;
2400 }
2401 
2402 //
2403 // Initialise system memory.
2404 //
MemInit(MRCParams_t * mrc_params)2405 void MemInit(
2406   MRCParams_t *mrc_params)
2407 {
2408   static const MemInit_t init[] =
2409   {
2410     { 0x0101, bmCold|bmFast|bmWarm|bmS3, clear_self_refresh       }, //0
2411     { 0x0200, bmCold|bmFast|bmWarm|bmS3, prog_ddr_timing_control  }, //1  initialise the MCU
2412     { 0x0103, bmCold|bmFast            , prog_decode_before_jedec }, //2
2413     { 0x0104, bmCold|bmFast            , perform_ddr_reset        }, //3
2414     { 0x0300, bmCold|bmFast       |bmS3, ddrphy_init              }, //4  initialise the DDRPHY
2415     { 0x0400, bmCold|bmFast            , perform_jedec_init       }, //5  perform JEDEC initialisation of DRAMs
2416     { 0x0105, bmCold|bmFast            , set_ddr_init_complete    }, //6
2417     { 0x0106,        bmFast|bmWarm|bmS3, restore_timings          }, //7
2418     { 0x0106, bmCold                   , default_timings          }, //8
2419     { 0x0500, bmCold                   , rcvn_cal                 }, //9  perform RCVN_CAL algorithm
2420     { 0x0600, bmCold                   , wr_level                 }, //10  perform WR_LEVEL algorithm
2421     { 0x0120, bmCold                   , prog_page_ctrl           }, //11
2422     { 0x0700, bmCold                   , rd_train                 }, //12  perform RD_TRAIN algorithm
2423     { 0x0800, bmCold                   , wr_train                 }, //13  perform WR_TRAIN algorithm
2424     { 0x010B, bmCold                   , store_timings            }, //14
2425     { 0x010C, bmCold|bmFast|bmWarm|bmS3, enable_scrambling        }, //15
2426     { 0x010D, bmCold|bmFast|bmWarm|bmS3, prog_ddr_control         }, //16
2427     { 0x010E, bmCold|bmFast|bmWarm|bmS3, prog_dra_drb             }, //17
2428     { 0x010F,               bmWarm|bmS3, perform_wake             }, //18
2429     { 0x0110, bmCold|bmFast|bmWarm|bmS3, change_refresh_period    }, //19
2430     { 0x0111, bmCold|bmFast|bmWarm|bmS3, set_auto_refresh         }, //20
2431     { 0x0112, bmCold|bmFast|bmWarm|bmS3, ecc_enable               }, //21
2432     { 0x0113, bmCold|bmFast            , memory_test              }, //22
2433     { 0x0114, bmCold|bmFast|bmWarm|bmS3, lock_registers           }  //23 set init done
2434   };
2435 
2436   uint32_t i;
2437 
2438   ENTERFN();
2439 
2440   DPF(D_INFO, "Meminit build %s %s\n", __DATE__, __TIME__);
2441 
2442   // MRC started
2443   post_code(0x01, 0x00);
2444 
2445   if (mrc_params->boot_mode != bmCold)
2446   {
2447     if (mrc_params->ddr_speed != mrc_params->timings.ddr_speed)
2448     {
2449       // full training required as frequency changed
2450       mrc_params->boot_mode = bmCold;
2451     }
2452   }
2453 
2454   for (i = 0; i < MCOUNT(init); i++)
2455   {
2456     uint64_t my_tsc;
2457 
2458 #ifdef MRC_SV
2459     if (mrc_params->menu_after_mrc && i > 14)
2460     {
2461       uint8_t ch;
2462 
2463       mylop:
2464 
2465       DPF(D_INFO, "-- c - continue --\n");
2466       DPF(D_INFO, "-- j - move to jedec init --\n");
2467       DPF(D_INFO, "-- m - memory test --\n");
2468       DPF(D_INFO, "-- r - cpu read --\n");
2469       DPF(D_INFO, "-- w - cpu write --\n");
2470       DPF(D_INFO, "-- b - hte base test --\n");
2471       DPF(D_INFO, "-- g - hte extended test --\n");
2472 
2473       ch = mgetc();
2474       switch (ch)
2475       {
2476       case 'c':
2477         break;
2478       case 'j':  //move to jedec init
2479         i = 5;
2480         break;
2481 
2482       case 'M':
2483       case 'N':
2484         {
2485     uint32_t n, res, cnt=0;
2486 
2487     for(n=0; mgetch()==0; n++)
2488     {
2489       if( ch == 'M' || n % 256 == 0)
2490       {
2491         DPF(D_INFO, "n=%d e=%d\n", n, cnt);
2492       }
2493 
2494       res = 0;
2495 
2496       if( ch == 'M')
2497       {
2498         memory_test(mrc_params);
2499         res |= mrc_params->status;
2500             }
2501 
2502       mrc_params->hte_setup = 1;
2503             res |= check_bls_ex(mrc_params, 0x00000000);
2504             res |= check_bls_ex(mrc_params, 0x00000000);
2505             res |= check_bls_ex(mrc_params, 0x00000000);
2506             res |= check_bls_ex(mrc_params, 0x00000000);
2507 
2508       if( mrc_params->rank_enables & 2)
2509       {
2510         mrc_params->hte_setup = 1;
2511               res |= check_bls_ex(mrc_params, 0x40000000);
2512               res |= check_bls_ex(mrc_params, 0x40000000);
2513               res |= check_bls_ex(mrc_params, 0x40000000);
2514               res |= check_bls_ex(mrc_params, 0x40000000);
2515       }
2516 
2517       if( res != 0)
2518       {
2519               DPF(D_INFO, "###########\n");
2520               DPF(D_INFO, "#\n");
2521               DPF(D_INFO, "# Error count %d\n", ++cnt);
2522               DPF(D_INFO, "#\n");
2523               DPF(D_INFO, "###########\n");
2524       }
2525 
2526     } // for
2527 
2528           select_memory_manager(mrc_params);
2529   }
2530         goto mylop;
2531       case 'm':
2532         memory_test(mrc_params);
2533         goto mylop;
2534       case 'n':
2535         cpu_memory_test(mrc_params);
2536         goto mylop;
2537 
2538       case 'l':
2539         ch = mgetc();
2540         if (ch <= '9') DpfPrintMask ^= (ch - '0') << 3;
2541         DPF(D_INFO, "Log mask %x\n", DpfPrintMask);
2542         goto mylop;
2543       case 'p':
2544         print_timings(mrc_params);
2545         goto mylop;
2546       case 'R':
2547         rd_train(mrc_params);
2548         goto mylop;
2549       case 'W':
2550         wr_train(mrc_params);
2551         goto mylop;
2552 
2553       case 'r':
2554         cpu_read();
2555         goto mylop;
2556       case 'w':
2557         cpu_write();
2558         goto mylop;
2559 
2560       case 'g':
2561         {
2562         uint32_t result;
2563         select_hte(mrc_params);
2564         mrc_params->hte_setup = 1;
2565         result = check_bls_ex(mrc_params, 0);
2566         DPF(D_INFO, "Extended test result %x\n", result);
2567         select_memory_manager(mrc_params);
2568         }
2569         goto mylop;
2570       case 'b':
2571         {
2572         uint32_t result;
2573         select_hte(mrc_params);
2574         mrc_params->hte_setup = 1;
2575         result = check_rw_coarse(mrc_params, 0);
2576         DPF(D_INFO, "Base test result %x\n", result);
2577         select_memory_manager(mrc_params);
2578         }
2579         goto mylop;
2580       case 'B':
2581         select_hte(mrc_params);
2582         HteMemOp(0x2340, 1, 1);
2583         select_memory_manager(mrc_params);
2584         goto mylop;
2585 
2586       case '3':
2587         {
2588         RegDPMC0 DPMC0reg;
2589 
2590         DPF( D_INFO, "===>> Start suspend\n");
2591         isbR32m(MCU, DSTAT);
2592 
2593         DPMC0reg.raw = isbR32m(MCU, DPMC0);
2594         DPMC0reg.field.DYNSREN = 0;
2595         DPMC0reg.field.powerModeOpCode = 0x05;    // Disable Master DLL
2596         isbW32m(MCU, DPMC0, DPMC0reg.raw);
2597 
2598         // Should be off for negative test case verification
2599         #if 1
2600         Wr32(MMIO, PCIADDR(0,0,0,SB_PACKET_REG),
2601             (uint32_t)SB_COMMAND(SB_SUSPEND_CMND_OPCODE, MCU, 0));
2602         #endif
2603 
2604         DPF( D_INFO, "press key\n");
2605         mgetc();
2606         DPF( D_INFO, "===>> Start resume\n");
2607         isbR32m(MCU, DSTAT);
2608 
2609         mrc_params->boot_mode = bmS3;
2610         i = 0;
2611         }
2612 
2613       } // switch
2614 
2615     } // if( menu
2616 #endif //MRC_SV
2617 
2618     if (mrc_params->boot_mode & init[i].boot_path)
2619     {
2620       uint8_t major = init[i].post_code >> 8 & 0xFF;
2621       uint8_t minor = init[i].post_code >> 0 & 0xFF;
2622       post_code(major, minor);
2623 
2624       my_tsc = read_tsc();
2625       init[i].init_fn(mrc_params);
2626       DPF(D_TIME, "Execution time %llX", read_tsc() - my_tsc);
2627     }
2628   }
2629 
2630   // display the timings
2631   print_timings(mrc_params);
2632 
2633   // MRC is complete.
2634   post_code(0x01, 0xFF);
2635 
2636   LEAVEFN();
2637   return;
2638 }
2639