| /* |
| * Copyright (C) 2016 Freescale Semiconductor, Inc. |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| * |
| * Refer doc/README.imximage for more details about how-to configure |
| * and create imximage boot image |
| * |
| * The syntax is taken as close as possible with the kwbimage |
| */ |
| /* image version */ |
| |
| #define __ASSEMBLY__ |
| |
| /* C header files modified with the expand_c_define.sh script from |
| source files in |
| ../../imx-sc-firmware/firmware/platform/board/mx8qm_val/ddrc/ |
| or verification BOM: |
| testbench/blocks/soc_tb/tool_data/compiler/include/drc.h |
| */ |
| #include <ddrc_mem_map.h> |
| #include <ddr_phy_mem_map.h> |
| |
| /* |
| * Device Configuration Data (DCD) |
| * |
| * Each entry must have the format: |
| * Addr-type Address Value |
| * |
| * where: |
| * Addr-type register length (1,2 or 4 bytes) |
| * Address absolute address of the register |
| * value value to be stored in the register |
| */ |
| |
| /* from file: imx-sc-firmware/firmware/platform/board/mx8qm_val/ddrc/ss_drc_lpddr4_init_zebu.c mx8qm_val/ddrc/ss_drc_lpddr4_init_zebu.c */ |
| /* copy/paste from ddrc_init() */ |
| |
| /* Mike comment #1: added DRC bringup */ |
| /* From BootROM team: */ |
| /* BootROM reserved 0xFF000000 ~ 0xFFFFFFFF as DRC bringup command purpose. |
| BootROM will not actually write values to that address range, but call |
| ROM API to bring up DRC. |
| If DCD address’s highest 8bit is 0xFF, BootROM will not actually access |
| that address but takes them as a ROM API call. |
| The lower 24bit are mapped as: |
| |
| Bit23-20: API type. |
| 1-ROM_API_BRINGUP_SS (Support DRC_0/1 only); |
| 8- ROM_API_AI_WR; |
| 9- ROM_API_AI_RD; |
| 0xF- ROM_API_UDELAY; |
| Bit19-14: DSC_ID. |
| Relative to ROM_API_BRINGUP_SS/ ROM_API_AI_WR/ ROM_API_AI_RD |
| Bit13-9: AI SRC SEL. |
| Relative to ROM_API_AI_WR/ ROM_API_AI_RD |
| Bit8-0: AI Register offset. |
| Relative to ROM_API_AI_WR/ ROM_API_AI_RD |
| |
| The date in DCD pair will be parsed as follow; |
| Bit7-0: delay value, in ms, if API is ROM_API_UDELAY |
| Bit7-0: PLL Integer which will be programed into PLL register. |
| For ROM_API_BRINGUP_SS. |
| Bit13-10: CSLICE_SRC_SEL. To specify the clock source of DRC CSLICE. |
| For ROM_API_BRINGUP_SS only. |
| |
| [JF] DRC cslice source coded in bit10-bit13 |
| #define BRINGUP_SS_CSLICE_SRC_SEL(x) (((x)>>10)&0xF) |
| |
| And definition as follow: |
| Bits[13:10]: |
| 0000 = xtal24M |
| 0001 = PLL DIV1 |
| 0010 = PLL DIV2 |
| 0011 = PLL DIV4 |
| |
| [MJ] lower bits (bits[7:0]) encoding scheme. |
| |
| Bits[7:0]: PLL multiplier. PLL_Freq = 24 MHz * multiplier / 2. |
| 0xC8 => 200, PLL_FREQ = 2400MHz. |
| 0x85 => 133, PLL_FREQ = 1596MHz. |
| |
| So this confirms a settings of 0x00000C85 gives a PLL speed of 1600MHz |
| with DIV4 to yield 800MHz. |
| |
| [MK] Previous setting of 0x00000cc8: PLL to 2400MHz with a DIV2 to yield 1200MHz. |
| Nitin had noticed this which started the discussion on setting this |
| to be 1600MHz (0x00000885) which aligned to the eventual target of |
| MX8QM LPDDR4. |
| So basically they are asking if we can go back to the previous |
| PLL:2400MHz/DDR:1200MHz setting (0x00000cc8). |
| |
| [MK] I was actually able to get confirmation from our local software team. |
| So it is ok to go ahead and modify the DCD. |
| |
| However, as Stephane pointed out, it is unlikely that 1600MHz will |
| work out of the gate for initial silicon so we can put in the two |
| options as follows: |
| |
| // For 1600MHz DDR operation, uncomment the following |
| DATA 4 0xff148000 0x00000885 // DRC0 bringup |
| DATA 4 0xff1a0000 0x00000885 // DRC1 bringup |
| |
| // For 800MHz DDR operation, uncomment the following |
| //DATA 4 0xff148000 0x00000C85 // DRC0 bringup |
| //DATA 4 0xff1a0000 0x00000C85 // DRC1 bringup |
| |
| And by default, we’ll have the 1600Mhz operation for zebu purposes |
| (I think some folks may do some performance analysis on zebu so having |
| the higher speed may be more beneficial). |
| |
| */ |
| /* For 1600MHz DDR operation (Zebu) */ |
| DATA 4 0xff148000 0x00000885 /* DRC0 bringup */ |
| DATA 4 0xff1a0000 0x00000885 /* DRC1 bringup */ |
| |
| /* DRAM 0 controller configuration begin */ |
| /* Program the umctl2 registers */ |
| |
| /* ddrc_lpddr4_init(0) */ |
| /* begin unroll ddrc_lpddr4_init(0) */ |
| /* ddrc_lpddr4_init - DRAM controller initialization */ |
| // This is for lpddr4 controller 800MHz and ddr 1600MHz |
| DATA 4 DDRC_MSTR_0 0xC3080020 // Set LPDDR4, BL = 16 and active ranks |
| |
| #ifdef DDRC_POSTED_REFRESH_EN |
| DATA 4 DDRC_RFSHCTL0_0 0x00210070 // posted refresh |
| #endif |
| #ifdef DDRC_REFRESH_PER_BANK_EN |
| SET_BIT 4 DDRC_RFSHCTL0_0 (1<<2) |
| DATA 4 DDRC_RFSHTMG_0 0x000C006F // treflpb=488ns*2/1.26264/2/32, |
| #else |
| DATA 4 DDRC_RFSHTMG_0 0x006100E0 // tREFI, tRFC |
| #endif |
| // CAST32(DDRC_INIT0_0 0x4002061A // skip_dram_init, post_cke, pre_cke |
| DATA 4 DDRC_INIT0_0 0x40020010 // pre_cke = 2ms is too long - LPDDR4 model hacked for 20us |
| // CAST32(DDRC_INIT1_0 0x009d0000 // dram_rstn = 200us; |
| DATA 4 DDRC_INIT1_0 0x00100000 // dram_rstn - LPDDR4 model hacked for 20us; |
| DATA 4 DDRC_INIT3_0 0x0054002d // MR1=0x54: nWR=30 BL=16; MR2=0x2d: RL=28 WL=14 |
| DATA 4 DDRC_INIT4_0 0x00310000 // MR3, MR13 |
| DATA 4 DDRC_RANKCTL_0 0x000006cf // diff_rank_wr_gap, diff_rank_rd_gap, max_rank_rd |
| DATA 4 DDRC_DRAMTMG0_0 0x1a201b22 // wr2pr, tFAW, tRASmax, tRASmin |
| DATA 4 DDRC_DRAMTMG1_0 0x00060633 // tXP, rd2pre, tRC |
| /* Mike comment #2 */ |
| /* RD2WR and WR2RD calculation: |
| RD2WR: RL + BL/2 + RU(tDQSCKmax/tCK) + WR_PREAMBLE + RD_POSTAMBLE - WL |
| RU[(28+16/2+RU(3.5/0.625)+2+0.5-14)/2] = |
| RU[(28+8+6+2+0.5-14)/2] = RU[15.25] = 16 (0x10) |
| |
| WR2RD: CWL + BL/2 + tWTR + 1(spec says to add one extra clock cycle) |
| RU[(14+8+16+1)/2)] = 20 (0x14) |
| */ |
| DATA 4 DDRC_DRAMTMG2_0 0x070E1014 // WL, RL, rd2wr, wr2rd |
| DATA 4 DDRC_DRAMTMG3_0 0x0170c00c // tmrw, tmrd, tmod |
| /* Mike and Stephane comment #3 */ |
| /* T_RP calculation: |
| T_RP: all bank precharge time of 21ns opposed to pre-bank 18ns |
| 21ns @ 1.6GHz = 33.6 -> RU[33.6/2] = 17 (0x11) |
| 18ns @ 1.6GHz = 28.8 -> RU[28.8/2] = 15 (0x0F) |
| -> we use per-bank |
| [SC] t_rp description is |
| “Minimum time from precharge to activate of same bank”. |
| So for me they refer to a single bank and not all bank. |
| */ |
| DATA 4 DDRC_DRAMTMG4_0 0x0f04080F // trcd, tccd, trrd, trp |
| /* Mike and Stephane comment #4 */ |
| /* T_CKSRX and T_CKSRE calculatin: |
| T_CKSRX: LPDDR4 tCKCKEH=3cks; RU(3/2)= 2 |
| T_CKSRE: LPDDR4 tCKCKEL, per JEDEC, max(5ns,3tCK) |
| 5ns@1.6GHz=8clks, RU(8/2) = 4 |
| -> use max(5ns, 5tCK). |
| [SC] tCKCKEL is different for power down entry. |
| As per JEDEC it is Max(5ns, 5nCK) there is another table in JEDEC spec for |
| power-down entry timings. Note that it seems there is a typo in JEDEC |
| because they call it tCKELCK. |
| Thus it gives RU(8/2) = 4 |
| |
| [MK] I noticed the Synopsys spec describes this as |
| “This is the time after Self Refresh Down Entry that CK is maintained as a valid clock. Specifies the clock disable delay after SRE.” |
| The Self Refresh AC timing table seems to have a closely matched description: |
| “Valid Clock Requirement after CKE Input low: tCKCKEL” which is |
| max(7.5ns, 3tck). For “tCKELCK”, I can see this is described in |
| the Power Down AC timing but the Synopsys spec did not mention power |
| down, just self refresh. Is it possible that the spec is incorrect |
| and should really be referring to the power down AC timing or maybe |
| I am misinterpreting the Synopsys spec? |
| |
| [SC] Basically when CKE goes low we enter in power down mode. Depending |
| on the state of the memory before CKE low we enter different power |
| down modes. You can refer to chapter 4.41.1 (FYI I have this version: |
| JESD209-4A from November 2015): |
| “If power-down occurs when all banks are idle, this mode is referred to as idle power-down. if power-down occurs when there is a row active in any bank, this mode is referred to as active power-down. And If power-down occurs when Self Refresh is in progress, this mode is referred to as Self Refresh power-down in which the internal refresh is continuing in the same way as Self Refresh mode.” |
| The tCKELCK appears in 2 tables: one linked to command bus training |
| (max(7.5ns, 3tck)) and one for Power-Down (Max(5ns, 5nCK)). So I |
| guess the timing in “Power-down AC Timing” table should apply. |
| Moreover the command bus training is fully done by software and thus |
| there is no need to have this timing is a register. |
| */ |
| DATA 4 DDRC_DRAMTMG5_0 0x02040C0C // tCKCKEH, tCKCKEL, tckesr, tcke |
| DATA 4 DDRC_DRAMTMG6_0 0x02020007 // tckdpde, tckdpdx, tckcsx |
| /* Mike and Stephane comment #5 */ |
| /* T_CKPDE calculation: |
| T_CKPDE: the spec says to use tCKCKEL. |
| [SC] tCKCKEL is different for power down entry. |
| As per JEDEC it is Max(5ns, 5nCK) |
| */ |
| DATA 4 DDRC_DRAMTMG7_0 0x00000401 // tckpde, tckpdx |
| DATA 4 DDRC_DRAMTMG12_0 0x00020610 // tCMDCKE, tCKEHCMD (=tXP?) |
| DATA 4 DDRC_DRAMTMG13_0 0x0c100002 // tODTLoff, tCCDMW, tPPD |
| DATA 4 DDRC_DRAMTMG14_0 0x000000E6 // txsr |
| DATA 4 DDRC_ZQCTL0_0 0x03200018 // tZQCAL, tZQLAT |
| DATA 4 DDRC_ZQCTL1_0 0x02800100 // tZQReset, tzq_short_interval |
| DATA 4 DDRC_DFITMG0_0 0x049C820C // dfi_t_ctrl_delay, dfi_t_rddata_en, dfi_tphy_wrdata, dfi_tphy_wrlat |
| DATA 4 DDRC_DFITMG1_0 0x00060303 // dfi_t_wrdata_delay, dfi_t_dram_clk_disable, dfi_t_dram_clk_enable |
| DATA 4 DDRC_DFITMG2_0 0x00001A0A // dfi_tphy_rdcslat, dfi_tphy_wrcslat |
| DATA 4 DDRC_DFIMISC_0 0x00000005 // dfi_data_cs_polarity |
| DATA 4 DDRC_DFIUPD0_0 0x80400003 // Disable the automatic dfi_ctrlupd_req generation |
| DATA 4 DDRC_DFIUPD1_0 0x00010002 // dfi_ctrlupd_req generation interval generation (min and max) |
| DATA 4 DDRC_DFIUPD2_0 0x80000000 // dfi_phyupd_en |
| DATA 4 DDRC_ADDRMAP0_0 0x00000017 // addrmap_cs_bit0 |
| /* Mike and Stephane comment #6 */ |
| /* Added DDRC_ADDRMAP4_0. |
| [MK] recommends the following line to make sure un-used columns are not activated: |
| This will help us with the DDR stress test. Eventually, we’ll add in the |
| capability to calculate a device’s density from the ADDRMAP registers. |
| But in order to correctly calculate, we need to set unused column bits to |
| 0xF. |
| This was something we actually discovered when developing the stress test |
| for MX7D (which uses a Synopsys controller for LPDDR3). |
| */ |
| DATA 4 DDRC_ADDRMAP4_0 0x00000F0F // addrmap_col_b10 and addrmap_col_b11 set to de-activated |
| #ifdef DISABLE_DDRC_BANK_INTERLEAVE |
| DATA 4 DDRC_ADDRMAP1_0 0x00181818 // addrmap_bank_b2, addrmap_bank_b1, addrmap_bank_b0 |
| DATA 4 DDRC_ADDRMAP5_0 0x04040404 // addrmap_row_b11, addrmap_row_b10_b2, addrmap_row_b1, addrmap_row_b0 |
| DATA 4 DDRC_ADDRMAP6_0 0x04040404 // addrmap_row_b15, addrmap_row_b14, addrmap_row_b13, addrmap_row_b12 |
| #else |
| DATA 4 DDRC_ADDRMAP1_0 0x00080808 // addrmap_bank_b2, addrmap_bank_b1, addrmap_bank_b0 |
| DATA 4 DDRC_ADDRMAP5_0 0x07070707 // addrmap_row_b11, addrmap_row_b10_b2, addrmap_row_b1, addrmap_row_b0 |
| DATA 4 DDRC_ADDRMAP6_0 0x07070707 // addrmap_row_b15, addrmap_row_b14, addrmap_row_b13, addrmap_row_b12 |
| #endif |
| |
| //CAST32(DDRC_ODTCFG_0 0x0a020b28 // wr_odt_hold, wr_odt_delay, rd_odt_hold, rd_odt_delay |
| DATA 4 DDRC_ODTMAP_0 0x00002211 // rank[3:0]_wr_odt, rank[3:0]_wr_odt |
| |
| DATA 4 DDRC_PCTRL_0_0 0x00000001 // Enable port 0 |
| |
| // In prevision of low frequency switch. DFITMG0.xxx_use_sdr fields can only |
| // be written when controller is under reset. |
| DATA 4 DDRC_DFITMG0_SHADOW_0 0x00808000 |
| |
| DATA 4 DDRC_PWRCTL_0 0x10D |
| |
| DATA 4 DDRC_PWRCTL_1 0x10D |
| |
| /* end unroll ddrc_lpddr4_init(0) */ |
| /* ddrc_lpddr4_init(1) */ |
| /* begin unroll ddrc_lpddr4_init(1) */ |
| /* ddrc_lpddr4_init - DRAM controller initialization */ |
| // This is for lpddr4 controller 800MHz and ddr 1600MHz |
| DATA 4 DDRC_MSTR_1 0xC3080020 // Set LPDDR4, BL = 16 and active ranks |
| |
| #ifdef DDRC_POSTED_REFRESH_EN |
| DATA 4 DDRC_RFSHCTL0_1 0x00210070 // posted refresh |
| #endif |
| #ifdef DDRC_REFRESH_PER_BANK_EN |
| SET_BIT 4 DDRC_RFSHCTL0_0 (1<<2) |
| DATA 4 DDRC_RFSHTMG_1 0x000C006F // treflpb=488ns*2/1.26264/2/32, |
| #else |
| DATA 4 DDRC_RFSHTMG_1 0x006100E0 // tREFI, tRFC |
| #endif |
| // CAST32(DDRC_INIT0_1 0x4002061A // skip_dram_init, post_cke, pre_cke |
| DATA 4 DDRC_INIT0_1 0x40020010 // pre_cke = 2ms is too long - LPDDR4 model hacked for 20us |
| // CAST32(DDRC_INIT1_1 0x009d0000 // dram_rstn = 200us; |
| DATA 4 DDRC_INIT1_1 0x00100000 // dram_rstn - LPDDR4 model hacked for 20us; |
| DATA 4 DDRC_INIT3_1 0x0054002d // MR1=0x54: nWR=30 BL=16; MR2=0x2d: RL=28 WL=14 |
| DATA 4 DDRC_INIT4_1 0x00310000 // MR3, MR13 |
| DATA 4 DDRC_RANKCTL_1 0x000006cf // diff_rank_wr_gap, diff_rank_rd_gap, max_rank_rd |
| DATA 4 DDRC_DRAMTMG0_1 0x1a201b22 // wr2pr, tFAW, tRASmax, tRASmin |
| DATA 4 DDRC_DRAMTMG1_1 0x00060633 // tXP, rd2pre, tRC |
| DATA 4 DDRC_DRAMTMG2_1 0x070E1014 // WL, RL, rd2wr, wr2rd |
| DATA 4 DDRC_DRAMTMG3_1 0x0170c00c // tmrw, tmrd, tmod |
| DATA 4 DDRC_DRAMTMG4_1 0x0f04080F // trcd, tccd, trrd, trp |
| DATA 4 DDRC_DRAMTMG5_1 0x02040C0C // tCKCKEH, tCKCKEL, tckesr, tcke |
| DATA 4 DDRC_DRAMTMG6_1 0x02020007 // tckdpde, tckdpdx, tckcsx |
| DATA 4 DDRC_DRAMTMG7_1 0x00000401 // tckpde, tckpdx |
| DATA 4 DDRC_DRAMTMG12_1 0x00020610 // tCMDCKE, tCKEHCMD (=tXP?) |
| DATA 4 DDRC_DRAMTMG13_1 0x0c100002 // tODTLoff, tCCDMW, tPPD |
| DATA 4 DDRC_DRAMTMG14_1 0x000000E6 // txsr |
| DATA 4 DDRC_ZQCTL0_1 0x03200018 // tZQCAL, tZQLAT |
| DATA 4 DDRC_ZQCTL1_1 0x02800100 // tZQReset, tzq_short_interval |
| DATA 4 DDRC_DFITMG0_1 0x049C820C // dfi_t_ctrl_delay, dfi_t_rddata_en, dfi_tphy_wrdata, dfi_tphy_wrlat |
| DATA 4 DDRC_DFITMG1_1 0x00060303 // dfi_t_wrdata_delay, dfi_t_dram_clk_disable, dfi_t_dram_clk_enable |
| DATA 4 DDRC_DFITMG2_1 0x00001A0A // dfi_tphy_rdcslat, dfi_tphy_wrcslat |
| DATA 4 DDRC_DFIMISC_1 0x00000005 // dfi_data_cs_polarity |
| DATA 4 DDRC_DFIUPD0_1 0x80400003 // Disable the automatic dfi_ctrlupd_req generation |
| DATA 4 DDRC_DFIUPD1_1 0x00010002 // dfi_ctrlupd_req generation interval generation (min and max) |
| DATA 4 DDRC_DFIUPD2_1 0x80000000 // dfi_phyupd_en |
| DATA 4 DDRC_ADDRMAP0_1 0x00000017 // addrmap_cs_bit0 |
| DATA 4 DDRC_ADDRMAP4_1 0x00000F0F // addrmap_col_b10 and addrmap_col_b11 set to de-activated |
| #ifdef DISABLE_DDRC_BANK_INTERLEAVE |
| DATA 4 DDRC_ADDRMAP1_1 0x00181818 // addrmap_bank_b2, addrmap_bank_b1, addrmap_bank_b0 |
| DATA 4 DDRC_ADDRMAP5_1 0x04040404 // addrmap_row_b11, addrmap_row_b10_b2, addrmap_row_b1, addrmap_row_b0 |
| DATA 4 DDRC_ADDRMAP6_1 0x04040404 // addrmap_row_b15, addrmap_row_b14, addrmap_row_b13, addrmap_row_b12 |
| #else |
| DATA 4 DDRC_ADDRMAP1_1 0x00080808 // addrmap_bank_b2, addrmap_bank_b1, addrmap_bank_b0 |
| DATA 4 DDRC_ADDRMAP5_1 0x07070707 // addrmap_row_b11, addrmap_row_b10_b2, addrmap_row_b1, addrmap_row_b0 |
| DATA 4 DDRC_ADDRMAP6_1 0x07070707 // addrmap_row_b15, addrmap_row_b14, addrmap_row_b13, addrmap_row_b12 |
| #endif |
| DATA 4 DDRC_ODTMAP_1 0x00002211 // rank[3:0]_wr_odt, rank[3:0]_wr_odt |
| DATA 4 DDRC_PCTRL_0_1 0x00000001 // Enable port 0 |
| DATA 4 DDRC_DFITMG0_SHADOW_1 0x00808000 |
| /* end unroll ddrc_lpddr4_init(1) */ |
| |
| // Overwrite some settings |
| /* for (ddr_num=0;ddr_num<=1;ddr_num++) { } */ |
| /* DDR #0 */ |
| /* Set DFIMISC.dfi_init_complete_en to 0 to avoid controller init |
| sequence to start after reset release */ |
| CLR_BIT 4 DDRC_DFIMISC_0 0x00000001 |
| // As DRAM init sequence will be run by controller set 0x0 to skip_dram_init field |
| CLR_BIT 4 DDRC_INIT0_0 0xC0000000 |
| /* DDR #1 */ |
| CLR_BIT 4 DDRC_DFIMISC_1 0x00000001 |
| CLR_BIT 4 DDRC_INIT0_1 0xC0000000 |
| |
| // ZeBu DDR PHY initialization begin... |
| // Launch DDR PHY PLLINIT, DCAL and ZCAL |
| /* ddr_phy_lpddr4_phy_init(0); */ |
| /* begin unroll ddr_phy_lpddr4_phy_init(0); */ |
| |
| /* ddr_phy_lpddr4_phy_init - PHY initialization */ |
| |
| //------------------------------------------- |
| // Configure registers for PHY initialization |
| // Timings are computed for a PHY at 800MHz (DRAM at 1600MHz) |
| //-------------------------------=------------ |
| |
| // Set-up DRAM Configuration Register |
| DATA 4 DDR_PHY_DCR_0 0x0000040D // LPDDR4 selection with 8 bank |
| |
| // Set-up PHY General Configuration Register |
| // PGCR1,4,5,6,7 are untouched |
| DATA 4 DDR_PHY_PGCR0_0 0x87001E00 // Set ADCP=1 (Address Copy) |
| /* Mike and Stephane comment #7 */ |
| /* tREFPRD calculation: |
| [MK] the revision of the PUB book I have is ” PUB Version 1.50c” |
| [SC] We have to follow latest specification. Hence |
| “(9*3900/Dram_clk_period)-600” => 0xD941. |
| */ |
| DATA 4 DDR_PHY_PGCR2_0 0x00F0D941 // Set tREFPRD (9*3.904us - 600) |
| DATA 4 DDR_PHY_PGCR3_0 0x050A1080 // CKEN/CKNEN toggling and polarity |
| |
| // Set-up PHY Timing Register |
| // PTR2 is untouched |
| DATA 4 DDR_PHY_PTR0_0 0x64032010 // tPLLPD, tPLLGS, tPHYRST |
| // CAST32(DDR_PHY_PTR1(ddr_num)) = 0x4E201C20; // tPLLLOCK=25us, tPLLRST=9us |
| DATA 4 DDR_PHY_PTR1_0 0x0D701C20 // tPLLLOCK reduced to 4.3us, tPLLRST=9us |
| |
| // Set-up PLL Control Register |
| DATA 4 DDR_PHY_PLLCR0_0 0x08000000 // FREQSEL=8 |
| DATA 4 DDR_PHY_DX8SLbPLLCR0_0 0x08000000 |
| |
| // Set-up Impedance Control Register |
| DATA 4 DDR_PHY_ZQCR_0 0x001FEC58 // Set ODT_MODE=0b10(LPDDR4 stype pullup) |
| |
| // Set-up Impedance Controller Program Register |
| // ZQnPR0, ZQnPR1 are untouched, lpddr4 PD_REFSEL should not be default value, FIXME |
| /* end unroll ddr_phy_lpddr4_phy_init(0); */ |
| /* ddr_phy_launch_init(0, 0x40); */ |
| /* begin unroll ddr_phy_launch_init(0, 0x40); */ |
| // Set-up PHY Initialization Register |
| DATA 4 DDR_PHY_PIR_0 0x40 |
| |
| // Launch initialization (set bit 0) |
| DATA 4 DDR_PHY_PIR_0 0x41 |
| /* end unroll ddr_phy_launch_init(0, 0x40); */ |
| /* ddr_phy_lpddr4_phy_init(1); */ |
| /* begin unroll ddr_phy_lpddr4_phy_init(1); */ |
| |
| /* ddr_phy_lpddr4_phy_init - PHY initialization */ |
| |
| //------------------------------------------- |
| // Configure registers for PHY initialization |
| // Timings are computed for a PHY at 800MHz (DRAM at 1600MHz) |
| //-------------------------------=------------ |
| |
| // Set-up DRAM Configuration Register |
| DATA 4 DDR_PHY_DCR_1 0x0000040D // LPDDR4 selection with 8 bank |
| |
| // Set-up PHY General Configuration Register |
| // PGCR1,4,5,6,7 are untouched |
| DATA 4 DDR_PHY_PGCR0_1 0x87001E00 // Set ADCP=1 (Address Copy) |
| /* Mike and Stephane comment #7 */ |
| /* tREFPRD calculation: |
| [MK] the revision of the PUB book I have is ” PUB Version 1.50c” |
| [SC] We have to follow latest specification. Hence |
| “(9*3900/Dram_clk_period)-600” => 0xD941. |
| */ |
| DATA 4 DDR_PHY_PGCR2_1 0x00F0D941 // Set tREFPRD (9*3.904us - 600) |
| DATA 4 DDR_PHY_PGCR3_1 0x050A1080 // CKEN/CKNEN toggling and polarity |
| |
| // Set-up PHY Timing Register |
| // PTR2 is untouched |
| DATA 4 DDR_PHY_PTR0_1 0x64032010 // tPLLPD, tPLLGS, tPHYRST |
| // CAST32(DDR_PHY_PTR1(ddr_num)) = 0x4E201C20; // tPLLLOCK=25us, tPLLRST=9us |
| DATA 4 DDR_PHY_PTR1_1 0x0D701C20 // tPLLLOCK reduced to 4.3us, tPLLRST=9us |
| |
| // Set-up PLL Control Register |
| DATA 4 DDR_PHY_PLLCR0_1 0x08000000 // FREQSEL=8 |
| DATA 4 DDR_PHY_DX8SLbPLLCR0_1 0x08000000 |
| |
| // Set-up Impedance Control Register |
| DATA 4 DDR_PHY_ZQCR_1 0x001FEC58 // Set ODT_MODE=0b10(LPDDR4 stype pullup) |
| |
| // Set-up Impedance Controller Program Register |
| // ZQnPR0, ZQnPR1 are untouched, lpddr4 PD_REFSEL should not be default value, FIXME |
| /* end unroll ddr_phy_lpddr4_phy_init(1); */ |
| /* ddr_phy_launch_init(1, 0x40); */ |
| /* begin unroll ddr_phy_launch_init(1, 0x40); */ |
| // Set-up PHY Initialization Register |
| DATA 4 DDR_PHY_PIR_1 0x40 |
| |
| // Launch initialization (set bit 0) |
| DATA 4 DDR_PHY_PIR_1 0x41 |
| /* end unroll ddr_phy_launch_init(1, 0x40); */ |
| |
| // While PHY initialization is running, registers for DRAM initialization can be configured |
| /* ddr_phy_lpddr4_dram_init(0); */ |
| /* begin unroll ddr_phy_lpddr4_dram_init(0); */ |
| |
| /* ddr_phy_lpddr4_dram_init(0) DRAM initialization */ |
| |
| //------------------------------------------- |
| // Configure registers for DRAM initialization |
| //------------------------------------------- |
| |
| // Set-up Mode Register |
| // MR0, MR3, MR4, MR5 MR6 are untouched |
| DATA 4 DDR_PHY_MR1_0 0x54 // Set BL, WR-PRE, nWR, RPST |
| DATA 4 DDR_PHY_MR2_0 0x2D // Set RL=28/WL=14 |
| DATA 4 DDR_PHY_MR3_0 0x31 // Set drive strength (40 Ohms by default) |
| DATA 4 DDR_PHY_MR11_0 0x60 // Set CA ODT=RZQ/6 |
| DATA 4 DDR_PHY_MR22_0 0x10 // Set ODTE-CS=1 (overrides ODT_CA for CS1 as CS not shared between ranks) |
| |
| // Set-up DRAM Timing Parameters Register |
| // DTPR6 is untouched |
| DATA 4 DDR_PHY_DTPR0_0 0x1044220C // tRRD, tRAS, tRP, tRTP |
| /* Mike and Stephane comment #8 */ |
| /* [SC] As we have RTL 1.51a (and not 1.40a as I mentioned before), |
| tODTUP does not exist anymore in DTPR1 (I also looked at the RTL) |
| Note that tODTUP exists in DTPR5 register. So I get following value |
| for DTPR1 (tMOD reset value is 4): |
| DDR_PHY_DTPR1 = 0x28400417; // tWLMRD, tFAW, tMRD |
| */ |
| DATA 4 DDR_PHY_DTPR1_0 0x28400417 // tWLMRD, tFAW, tODTUP, tMRD |
| /* Mike and Stephane comment #9 */ |
| /* [MK] I am adding the following note in the DDR Register Programming aid for tVRCG: |
| For LPDDR4, we take the greater of (tVRCG_ENABLE,VRCG_DISABLE), which per JEDEC is (200ns, 100ns). So, 200ns converted to clock cycles (1.6GHz freq) yields 320 clocks, which yields a tVRCG bit setting of '100' or 4. |
| However, tVRCG-3 = 4 - 3 = 1, which is less than tVRCG_ENABLE/tVRCG_DISABLE or 200ns/100ns = 2. |
| So, recommend to set tVRCG to the next higher value of 5 (bit setting '101') or 384 clocks. |
| So I set DTPR2=0x006CA1CC |
| */ |
| DATA 4 DDR_PHY_DTPR2_0 0x006CA1CC // tRTW, tRTODT, tCMDCKE, tCKE, tVRCG, tXS |
| DATA 4 DDR_PHY_DTPR3_0 0x01800604 // tODX, tCCD, tDLLK, tDQSCKmax, tDQSCK (FIXME double check tDLLK) |
| /* Mike and Stephane comment #10 */ |
| /* [MK] The PHY spec states for tXP: |
| "Note: For LPDDR4, additional offset of +3 tCK must be added to the value calculated for the corresponding speed grade." |
| |
| [SC] What about the tWLO field ? You set it to 0. What follows the |
| tWLOmin requirement but description says: |
| “This must include the SDRAM tWLO timing parameter plus the round trip delay from control block to SDRAM back to control block.” |
| And I have ni idea how to calculate this round trip delay. So I kept |
| the default value. I will ask Synopsys. |
| |
| [MK] For tXP, I will make the update to +3 clocks (I confirm I get 0xF). |
| |
| [SC] Here is Synopsys’s Answer for DTPR4.tWLO: |
| "There is no need to change the default value, it has a significant margin, enough to cover the maximum round trip delay supported scenario.” |
| So I get DDR_PHY_DTPR4 = 0x01C02B0F; // tRFC, tWLO, tXP. |
| */ |
| DATA 4 DDR_PHY_DTPR4_0 0x01C02B0F // tRFC, tWLO, tXP |
| DATA 4 DDR_PHY_DTPR5_0 0x00651D10 // tRC, tRCD, tWTR |
| |
| // Set-up PHY Timing Register |
| // CAST32(DDR_PHY_PTR3(ddr_num)) = 0x0030D400; // tDINIT0 - 2ms |
| DATA 4 DDR_PHY_PTR3_0 0x00007D00 // tDINIT0 - memory model hacked to 20us |
| /* Mike and Stephane comment #11 */ |
| /* [MK] When we calculated this we came up with DATA 4 DDR_PHY_PTR4_0 0x00000C80: |
| 2000ns*1.6GHz=3200 -> convert to hex -> 0xC80 |
| [SC] I guess I did not get the 2000ns with 0xC80. That’s why I |
| increased it a little bit. But I may have wrong. I will make a trial |
| with 0xC80. |
| |
| [MK] Ok, let me know what you find out. |
| |
| [SC] 0xC80 works fine in my simulation. So I will keep it. |
| */ |
| DATA 4 DDR_PHY_PTR4_0 0x00000C80 // tDINIT1 (2000ns) |
| DATA 4 DDR_PHY_PTR5_0 0x00007D00 // tDINIT2 - normally 200us but memory model hacked to 20us |
| /* Mike and Stephane comment #12 */ |
| /* [MK] For tDINIT4, the PHY spec says: |
| "Note: For LPDDR4 tZQLAT, additional offset of +3 tCK must be added i.e. value programmed in this register field must be MAX(30ns, 8 tCK) + 3 tCK" |
| But we did not add 3 clocks, should we? |
| [SC] I have 0x03300641 in this register. Yes you need the +3 tCK. This was not stated in the 1.40a databook but I get issues and Synopsys confirmed me the +3 tCK are mandatory. |
| |
| [MK] Ok, I’ve added the “+3” in the register programming aid and I confirm I get “0x33” |
| |
| For tDINIT3, we calculated 0x640 as follows: |
| 1us*1.6Ghz= 1600 ->converted to hex -> 0x640 |
| |
| [SC] You’re correct but I guess I did not get the 1us with 0x640. |
| That’s why I increased it to 0x641. I will make a trial with 0x640. |
| |
| [MK] Thanks, let me know if 0x640 works. |
| |
| [SC] 0x640 works fine in my simulation. So I will keep it. |
| |
| [MK] Thanks, I will also keep 0x640 in the register programming aid. |
| */ |
| DATA 4 DDR_PHY_PTR6_0 0x03300640 // tDINIT4 (MAX(30ns, 8 tCK)+3 tCK), tDINIT3 (1us) |
| |
| // RDIMMGCR0-2 RDIMMGCR0-4?? |
| |
| // Set-up DATX8 Common Configuration Register |
| // DXCCR is untouched |
| |
| // Set-up DDR System General Configuration Register |
| // DSGCR is untouched |
| |
| // Set-up ODT Configuration Register |
| // DDR ODT_CA signal is tied at boundary of DDR. Thus no need to drive it dynamically. |
| DATA 4 DDR_PHY_RANKIDR_0 1 // Select rank 1 to write |
| DATA 4 DDR_PHY_ODTCR_0 0x00000000 // ODT of rank1 disabled |
| DATA 4 DDR_PHY_RANKIDR_0 0 // Select rank 0 to write |
| DATA 4 DDR_PHY_ODTCR_0 0x00000000 // ODT of rank0 disabled |
| |
| // Set-up Anti-Aging Control Register |
| // AACR is untouched |
| |
| // Set-up Data Training Address Register |
| // DTAR0-3 are untouched |
| // !! DTAR3 is not described in spec !! |
| |
| // Set-up AC I/O Configuration Register |
| // ACIOCR1-4 are untouched |
| DATA 4 DDR_PHY_ACIOCR0_0 0x30070800 // PNUM2 (i.e.LPDDR4) selection [10:11] = 0x2 |
| DATA 4 DDR_PHY_ACIOCR5_0 0x09000000 // I/O mode = LPDDR4 |
| // Due to address copy set A[13] (=cke_B[0]) and A[15] (=cke_B[1]) outputs as always ON. |
| DATA 4 DDR_PHY_ACIOCR1_0 0x44000000 |
| |
| // IOVCR0-1, DXnGCR0-4??, CALBYP |
| |
| // Set-up VREF Training Control Registers |
| DATA 4 DDR_PHY_VTCR0_0 0xF0032019 // CK1, CK0 |
| DATA 4 DDR_PHY_VTCR1_0 0x07F00173 // HVIO=1, SHREN=1, SHRNK=0 |
| |
| // Set-up DATX8 General Configuration Registers |
| // DXnGCR0-4 are untouched |
| |
| // Set-up DATX8 DX Control Register 2 |
| // PREOEX=2.5tCK (0.5 more than MR1), POSOEX=1tCK (0.5 more than in MR3), LPWAKEUP_THRSH=0xA |
| /* Mike comment #14 */ |
| /* [MK] Regarding chap 2.2.1. As I was reading through it some new |
| information was made aware to me and I wanted to pass it by you to get |
| your thoughts and opinions and maybe get Synopsys to weigh in: |
| 1. Preamble and Postamble extensions in DDR_PHY_DX8SLbDXCTL2, PREOES |
| and POSOEX. The spec says to program a value 0.5*tCK more than the value |
| required by the DRAM. It is re-iterated in chap 4.9.13. We currently |
| have these set to PREOEX=2 dram clock preamble, POSOEX=0.5 dram clock |
| postamble. These values are also the same values programmed into the |
| LPDDR4 MR1 register. So the question is, should be add 0.5 to the values |
| in PREOEX and POSOEX? |
| [SC] I have DDR_PHY_DX8SLbDXCTL2 = 0x001C1400. So it matches |
| the +0.5tCK. Laurent told me that it did not take my library for the |
| registers settings. Hence the few discrepancies you noticed. I think he |
| is now aligned. |
| |
| [MK] Thanks, I confirm I get the same value for PREOEX and POSOEX |
| when I add the additional 0.5*tCK, however for the setting of |
| “LPWAKEUP_THRSH”, the previous value was “4b1100” but it appears |
| to now be changed to “4b1010”. Honestly, after reading the bit |
| description I became more confused as to what this configures (actually, |
| many of the bit descriptions in the PHY are barely understandable). But |
| I was curious if you knew why this value was changed? |
| |
| [SC] The LPWAKEUP_THRSH indicates the time needed by PHY to come back |
| from low power mode. |
| When controller uMCTL2 requests for low power through the dfi_lp_ctrl_req |
| or dfi_lp_data_req pins, it also sends a signal named dfi_lp_wakeup. Then |
| there is a comparison between dfi_lp_wakeup and LPWAKEUP_THRSH. |
| If dfi_lp_wakeup > LPWAKEUP_THRSH then PHY goes in low power. |
| |
| Note that the dfi_lp_wakeup value is set through a controller register. So |
| basically I understand that the software should make an estimation on how |
| many times the low power mode will length. If this estimation is lower |
| than the LPWAKEUP_THRSH then the PHY will not go into low power mode as |
| it would take more time to wake-up than the estimated low power length. |
| |
| Regarding LPWAKEUP_THRSH value: |
| In RTL verification we can see that PLL lock time is 4.3us. So it |
| gives a LPWAKEUP_THRSH=0xA (4.3us/800MHz + 8*800). |
| In real world PLL lock time is given for 25us. So you should get |
| 25us/800MHz + 8*800 => 26400. Thus LPWAKEUP_THRSH=0xB. |
| |
| [MK] Thank you for the explanation. |
| For DDR_PHY_DX8SLbDXCTL2 I have 0x001C1400. We can probably close this one. |
| |
| 2. There is mention of drift detection feature being on. In chap 4.5.2.1, |
| there’s a statement “Drift detection is enabled by DQSDR0.DFTDTEN” |
| but we currently do not have this set does Synopsys feel that we should |
| set this? |
| [SC] I think we should have it but for RTL verification it was not |
| mandatory. I have to work on this for Silicon. |
| |
| 3. There’s mention of setting ODT in the DRAM for DQODT and CAODT |
| in MR11 along with setting MR22.CODT, however, the Synopsys controller |
| does not have registers for this. I am wondering if the defaults for |
| these are ok or if the controller automatically updates these in some |
| training algorithm? |
| [SC] I need to work on this as well. If some registers are not written |
| during DRAM init or through a training then we will need to write |
| them manually. |
| |
| 4. There’s talk of programming ZQnPR0, DQnPR0, and ZQnPR1 however, |
| our DCD do not touch these registers. Does Synopsys have any thoughts |
| as to how we should treat this? |
| [SC] I need to work on this as well |
| |
| [MK] Stephane and I agreed that we needed to add “0.5tck” for both |
| POSOEX and PREOEX. Also, LPWAKEUP_THRSH was updated. |
| It should be 0x001C1600. |
| It is different than in RTL verification due to the reduced time observed |
| for PLL lock in RTL (4.3us instead of 25us). |
| */ |
| DATA 4 DDR_PHY_DX8SLbDXCTL2_0 0x001C1600 |
| |
| // Set-up DATX8 IO Control Register |
| DATA 4 DDR_PHY_DX8SLbIOCR_0 0x09000000 // I/O mode = LPDDR4 |
| |
| // Set-up DATX8 DQS Control Register |
| /* Mike and Stephane comment #13 */ |
| /* [MK] The upper value of this register is written as 0x013, however, |
| per the PHY spec these bits are reserved. Is it a mistake in the spec? |
| Is the reserved value of these upper bits 0x013? |
| [SC] In databook 1.40a bit 21 is WRRMODE and bit 24 is RRRMODE. Both |
| need to be set at ‘1’. I will check with Synopsys whether or not |
| they are reserved for RTL 1.40a as well. |
| Bits [20:19] are DQSGX. It is set to 0x2. If I well remember I needed this |
| because almost all our verification testcases are run without training. |
| |
| [MK] Ok, let me know what you find out from Synopsys. BTW, for DQSGX, |
| in order to get “0x013E…”, should bits [20:19] be set to 11b (0x3)? |
| If set to “0x2” then the “E” becomes “6”? |
| |
| [SC] Synopsys did not really answer for WRRMODE and RRRMODE. So I ask |
| them again. |
| Anyway I looked into the RTL and the bitfields are there. Moreover they |
| are connected; so I would say they have an effect in our RTL. Hence I |
| would strongly recommend to write them to ‘1’. |
| Regarding DQSGX my mistake I set it to 0x3. |
| However I am not able to remember why I configure this register. As per |
| register description DQSRES and DQSNRES usage is for LPDDR3 or DDR3, |
| and DQSGX=0x3 seems to be required for LPDDR3 only. |
| Do you have more clues ? |
| |
| [MK] Regarding WRRMODE and RRRMODE, I will include these are reserved |
| bit field in the register programming aid and till set each to ‘1’ |
| to yield 0x013E4091. |
| Regarding DQSRES, DQSNRES, and DQSGX, you ask a very good question. |
| Admittedly I barely noticed that these applied to different memory |
| types other than LPDDR4. When I first created the register programming |
| aid, I simply applied to values found in the SCU firmware and assumed |
| whoever came up with these values knew more about the PHY than I did so |
| I left these as is. Would you mind also asking Synopsys about this? |
| Perhaps our documentation needs to be updated to include LPDDR4 for |
| these or we don’t program these bit fields and see what happens? |
| [SC] I ran 2 simulations without configuring this DDR_PHY_DX8SLbDQSCTL_0 |
| register and they passed. So it looks like we should not touch it. |
| Anyway I am still waiting feedback for the WRRMODE and RRRMODE. |
| Regarding DQSRES, DQSNRES, and DQSGX I found this in the databook |
| (chapter 2.2.1 LPDDR4 Usage, version 1.50c): |
| - DQS pulldown and DQSN pullup should be disabled. To do this, set |
| DX8SL*DQSCTL.DQSRES=0000 and set DX8SL*DQSCTL.DQSNRES=0000. Disabling |
| the pullup and pulldown provides optimal DQS/DQSN duty cycle on reads. |
| - Gate extension must be disabled (set DX8SL*DQSCTL.DQSGX=00) |
| |
| [MK] Regarding WRRMODE and RRRMODE : we’ll wait and see what Synopsys says |
| Regarding DQSRES, DQSNRES, and DQSGX: thanks for highlighting chap 2.2.1, |
| some very good info was conveyed there. I agree we should zero thee |
| parameters out. |
| So for now, should the register be programmed as: 0x01264000? |
| |
| [SC] I think so. I basically removed the configuration of this register |
| and I get no issues. From PHY RTL I can see the reset value as 0x01264000. |
| I am still in discussion with Synopsys to clarify the WRRMODE and |
| RRRMODE bitfields. |
| |
| [SC] Synopsys opened a ticket on their side to update databook. So these |
| fields exist and value should be ‘1’. |
| */ |
| DATA 4 DDR_PHY_DX8SLbDQSCTL_0 0x01264000 // DQS resistor |
| /* end unroll ddr_phy_lpddr4_dram_init(0); */ |
| /* ddr_phy_lpddr4_dram_init(1); */ |
| /* begin unroll ddr_phy_lpddr4_dram_init(1); */ |
| /* ddr_phy_lpddr4_dram_init(1) DRAM initialization */ |
| |
| //------------------------------------------- |
| // Configure registers for DRAM initialization |
| //------------------------------------------- |
| |
| // Set-up Mode Register |
| // MR0, MR3, MR4, MR5 MR6 are untouched |
| DATA 4 DDR_PHY_MR1_1 0x54 // Set BL, WR-PRE, nWR, RPST |
| DATA 4 DDR_PHY_MR2_1 0x2D // Set RL=28/WL=14 |
| DATA 4 DDR_PHY_MR3_1 0x31 // Set drive strength (40 Ohms by default) |
| DATA 4 DDR_PHY_MR11_1 0x60 // Set CA ODT=RZQ/6 |
| DATA 4 DDR_PHY_MR22_1 0x10 // Set ODTE-CS=1 (overrides ODT_CA for CS1 as CS not shared between ranks) |
| |
| // Set-up DRAM Timing Parameters Register |
| // DTPR6 is untouched |
| DATA 4 DDR_PHY_DTPR0_1 0x1044220C // tRRD, tRAS, tRP, tRTP |
| DATA 4 DDR_PHY_DTPR1_1 0x28400417 // tWLMRD, tFAW, tODTUP, tMRD |
| DATA 4 DDR_PHY_DTPR2_1 0x006CA1CC // tRTW, tRTODT, tCMDCKE, tCKE, tVRCG, tXS |
| DATA 4 DDR_PHY_DTPR3_1 0x01800604 // tODX, tCCD, tDLLK, tDQSCKmax, tDQSCK (FIXME double check tDLLK) |
| DATA 4 DDR_PHY_DTPR4_1 0x01C02B0F // tRFC, tWLO, tXP |
| DATA 4 DDR_PHY_DTPR5_1 0x00651D10 // tRC, tRCD, tWTR |
| |
| // Set-up PHY Timing Register |
| // CAST32(DDR_PHY_PTR3(ddr_num)) = 0x0030D400; // tDINIT0 - 2ms |
| DATA 4 DDR_PHY_PTR3_1 0x00007D00 // tDINIT0 - memory model hacked to 20us |
| DATA 4 DDR_PHY_PTR4_1 0x00000C80 // tDINIT1 (2000ns) |
| DATA 4 DDR_PHY_PTR5_1 0x00007D00 // tDINIT2 - normally 200us but memory model hacked to 20us |
| DATA 4 DDR_PHY_PTR6_1 0x03300640 // tDINIT4 (MAX(30ns, 8 tCK)+3 tCK), tDINIT3 (1us) |
| |
| // RDIMMGCR0-2 RDIMMGCR0-4?? |
| |
| // Set-up DATX8 Common Configuration Register |
| // DXCCR is untouched |
| |
| // Set-up DDR System General Configuration Register |
| // DSGCR is untouched |
| |
| // Set-up ODT Configuration Register |
| // DDR ODT_CA signal is tied at boundary of DDR. Thus no need to drive it dynamically. |
| DATA 4 DDR_PHY_RANKIDR_1 1 // Select rank 1 to write |
| DATA 4 DDR_PHY_ODTCR_1 0x00000000 // ODT of rank1 disabled |
| DATA 4 DDR_PHY_RANKIDR_1 0 // Select rank 0 to write |
| DATA 4 DDR_PHY_ODTCR_1 0x00000000 // ODT of rank0 disabled |
| |
| // Set-up Anti-Aging Control Register |
| // AACR is untouched |
| |
| // Set-up Data Training Address Register |
| // DTAR0-3 are untouched |
| // !! DTAR3 is not described in spec !! |
| |
| // Set-up AC I/O Configuration Register |
| // ACIOCR2-4 are untouched |
| DATA 4 DDR_PHY_ACIOCR0_1 0x30070800 // PNUM2 (i.e.LPDDR4) selection [10:11] = 0x2 |
| DATA 4 DDR_PHY_ACIOCR5_1 0x09000000 // I/O mode = LPDDR4 |
| // Due to address copy set A[13] (=cke_B[0]) and A[15] (=cke_B[1]) outputs as always ON. |
| DATA 4 DDR_PHY_ACIOCR1_1 0x44000000 |
| |
| // IOVCR0-1, DXnGCR0-4??, CALBYP |
| |
| // Set-up VREF Training Control Registers |
| DATA 4 DDR_PHY_VTCR0_1 0xF0032019 // CK1, CK0 |
| DATA 4 DDR_PHY_VTCR1_1 0x07F00173 // HVIO=1, SHREN=1, SHRNK=0 |
| |
| // Set-up DATX8 General Configuration Registers |
| // DXnGCR0-4 are untouched |
| |
| // Set-up DATX8 DX Control Register 2 |
| // PREOEX=2.5tCK (0.5 more than MR1), POSOEX=1tCK (0.5 more than in MR3), LPWAKEUP_THRSH=0xA |
| DATA 4 DDR_PHY_DX8SLbDXCTL2_1 0x001C1600 |
| |
| // Set-up DATX8 IO Control Register |
| DATA 4 DDR_PHY_DX8SLbIOCR_1 0x09000000 // I/O mode = LPDDR4 |
| |
| // Set-up DATX8 DQS Control Register |
| DATA 4 DDR_PHY_DX8SLbDQSCTL_1 0x01264000 // DQS resistor |
| /* end unroll ddr_phy_lpddr4_dram_init(1); */ |
| |
| |
| // Wait PHY initialization end then advise DDR PHY that DRAM init will be done by uMCTL2 |
| /* ddr_phy_wait_init_done(0); */ |
| /* begin unroll ddr_phy_wait_init_done(0); */ |
| |
| // Wait for bit 0 of PGSR0 to be '1' |
| CHECK_BITS_SET 4 DDR_PHY_PGSR0_0 0x1 0x1 |
| |
| // Check that no error occured |
| /* [LC] TODO: not sure if this check should be implemented |
| if ((CAST32(DDR_PHY_PGSR0(ddr_num)) & 0x7FFC0000) != 0x0) { |
| board_print(0, "DDR PHY - Initialization failed !\n"); |
| } |
| */ |
| /* end unroll ddr_phy_wait_init_done(0); */ |
| |
| /* ddr_phy_launch_init(0, 0x40000); */ |
| /* begin unroll ddr_phy_launch_init(0, 0x40000); */ |
| |
| // Set-up PHY Initialization Register |
| DATA 4 DDR_PHY_PIR_0 0x40000 |
| |
| // Launch initialization (set bit 0) |
| DATA 4 DDR_PHY_PIR_0 0x40001 |
| /* end unroll ddr_phy_launch_init(0, 0x40000); */ |
| |
| /* ddr_phy_wait_init_done(1); */ |
| /* begin unroll ddr_phy_wait_init_done(1); */ |
| |
| // Wait for bit 0 of PGSR0 to be '1' |
| CHECK_BITS_SET 4 DDR_PHY_PGSR0_1 0x1 0x1 |
| |
| // Check that no error occured |
| /* [LC] TODO: not sure if this check should be implemented |
| if ((CAST32(DDR_PHY_PGSR0(ddr_num)) & 0x7FFC0000) != 0x0) { |
| board_print(0, "DDR PHY - Initialization failed !\n"); |
| } |
| */ |
| /* end unroll ddr_phy_wait_init_done(1); */ |
| |
| /* ddr_phy_launch_init(1, 0x40000); */ |
| /* begin unroll ddr_phy_launch_init(1, 0x40000); */ |
| |
| // Set-up PHY Initialization Register |
| DATA 4 DDR_PHY_PIR_1 0x40000 |
| |
| // Launch initialization (set bit 0) |
| DATA 4 DDR_PHY_PIR_1 0x40001 |
| /* end unroll ddr_phy_launch_init(1, 0x40000); */ |
| |
| // Wait assertion of DDR PHY PGSR0.IDONE field acknowledging that DRAM init will be done by controller |
| /* ddr_phy_wait_init_done(0); */ |
| /* begin unroll ddr_phy_wait_init_done(0); */ |
| // Wait for bit 0 of PGSR0 to be '1' |
| CHECK_BITS_SET 4 DDR_PHY_PGSR0_0 0x1 0x1 |
| |
| // Check that no error occured |
| /* [LC] TODO: not sure if this check should be implemented |
| if ((CAST32(DDR_PHY_PGSR0(ddr_num)) & 0x7FFC0000) != 0x0) { |
| board_print(0, "DDR PHY - Initialization failed !\n"); |
| } |
| */ |
| /* end unroll ddr_phy_wait_init_done(0); */ |
| /* ddr_phy_wait_init_done(1); */ |
| /* begin unroll ddr_phy_wait_init_done(1); */ |
| // Wait for bit 0 of PGSR0 to be '1' |
| CHECK_BITS_SET 4 DDR_PHY_PGSR0_1 0x1 0x1 |
| |
| // Check that no error occured |
| /* [LC] TODO: not sure if this check should be implemented |
| if ((CAST32(DDR_PHY_PGSR0(ddr_num)) & 0x7FFC0000) != 0x0) { |
| board_print(0, "DDR PHY - Initialization failed !\n"); |
| } |
| */ |
| /* end unroll ddr_phy_wait_init_done(1); */ |
| |
| |