1478af8d3SJacky Bai /* 2478af8d3SJacky Bai * Copyright 2021-2024 NXP 3478af8d3SJacky Bai * 4478af8d3SJacky Bai * SPDX-License-Identifier: BSD-3-Clause 5478af8d3SJacky Bai */ 6478af8d3SJacky Bai 7478af8d3SJacky Bai #include <assert.h> 8478af8d3SJacky Bai #include <stdbool.h> 9478af8d3SJacky Bai 10caee2733SJacky Bai #include <arch_helpers.h> 11caee2733SJacky Bai #include <bl31/interrupt_mgmt.h> 12caee2733SJacky Bai #include <common/runtime_svc.h> 13478af8d3SJacky Bai #include <lib/mmio.h> 14caee2733SJacky Bai #include <lib/spinlock.h> 15caee2733SJacky Bai #include <plat/common/platform.h> 16caee2733SJacky Bai 17478af8d3SJacky Bai #include <platform_def.h> 18478af8d3SJacky Bai 19caee2733SJacky Bai #include <dram.h> 20478af8d3SJacky Bai #include <upower_api.h> 21478af8d3SJacky Bai 22478af8d3SJacky Bai #define PHY_FREQ_SEL_INDEX(x) ((x) << 16) 23478af8d3SJacky Bai #define PHY_FREQ_MULTICAST_EN(x) ((x) << 8) 24478af8d3SJacky Bai #define DENALI_PHY_1537 U(0x5804) 25478af8d3SJacky Bai 26478af8d3SJacky Bai #define IMX_DDRC_BASE U(0x2E060000) 27478af8d3SJacky Bai #define SAVED_DRAM_DATA_BASE U(0x20055000) 28478af8d3SJacky Bai #define DENALI_CTL_144 0x240 29478af8d3SJacky Bai #define LPI_WAKEUP_EN_SHIFT U(8) 30478af8d3SJacky Bai #define IMX_LPAV_SIM_BASE 0x2DA50000 31478af8d3SJacky Bai #define LPDDR_CTRL 0x14 32478af8d3SJacky Bai #define LPDDR_AUTO_LP_MODE_DISABLE BIT(24) 33478af8d3SJacky Bai #define SOC_LP_CMD_SHIFT U(15) 34478af8d3SJacky Bai #define LPDDR_CTRL2 0x18 35478af8d3SJacky Bai 36478af8d3SJacky Bai #define DENALI_CTL_00 U(0x0) 37478af8d3SJacky Bai #define DENALI_CTL_23 U(0x5c) 38478af8d3SJacky Bai #define DFIBUS_FREQ_INIT_SHIFT U(24) 39478af8d3SJacky Bai #define TSREF2PHYMSTR_SHIFT U(8) 40478af8d3SJacky Bai #define TSREF2PHYMSTR_MASK GENMASK(13, 8) 41478af8d3SJacky Bai 42478af8d3SJacky Bai #define DENALI_CTL_24 U(0x60) 43478af8d3SJacky Bai #define DENALI_CTL_25 U(0x64) 44478af8d3SJacky Bai 45478af8d3SJacky Bai #define DENALI_CTL_93 U(0x174) 46478af8d3SJacky Bai #define PWRUP_SREFRESH_EXIT BIT(0) 47478af8d3SJacky Bai 48478af8d3SJacky Bai #define DENALI_CTL_127 U(0x1fc) 49478af8d3SJacky Bai #define PHYMSTR_TRAIN_AFTER_INIT_COMPLETE BIT(16) 50478af8d3SJacky Bai 51478af8d3SJacky Bai #define DENALI_CTL_147 U(0x24c) 52478af8d3SJacky Bai #define DENALI_CTL_153 U(0x264) 53478af8d3SJacky Bai #define PCPCS_PD_EN BIT(8) 54478af8d3SJacky Bai 55478af8d3SJacky Bai #define DENALI_CTL_249 U(0x3E4) 56478af8d3SJacky Bai #define DENALI_CTL_266 U(0x428) 57478af8d3SJacky Bai 58478af8d3SJacky Bai #define DENALI_PHY_1547 U(0x582c) 59478af8d3SJacky Bai #define PHY_LP4_BOOT_DISABLE BIT(8) 60478af8d3SJacky Bai 61478af8d3SJacky Bai #define DENALI_PHY_1559 U(0x585c) 62478af8d3SJacky Bai #define DENALI_PHY_1590 U(0x58D8) 63478af8d3SJacky Bai 64478af8d3SJacky Bai #define DENALI_PI_00 U(0x2000) 65478af8d3SJacky Bai #define DENALI_PI_04 U(0x2010) 66478af8d3SJacky Bai #define DENALI_PI_52 U(0x20D0) 67478af8d3SJacky Bai #define DENALI_PI_26 U(0x2068) 68478af8d3SJacky Bai #define DENALI_PI_33 U(0x2084) 69478af8d3SJacky Bai #define DENALI_PI_65 U(0x2104) 70478af8d3SJacky Bai #define DENALI_PI_77 U(0x2134) 71478af8d3SJacky Bai #define DENALI_PI_134 U(0x2218) 72478af8d3SJacky Bai #define DENALI_PI_131 U(0x220C) 73478af8d3SJacky Bai #define DENALI_PI_132 U(0x2210) 74478af8d3SJacky Bai #define DENALI_PI_134 U(0x2218) 75478af8d3SJacky Bai #define DENALI_PI_137 U(0x2224) 76478af8d3SJacky Bai #define DENALI_PI_174 U(0x22B8) 77478af8d3SJacky Bai #define DENALI_PI_175 U(0x22BC) 78478af8d3SJacky Bai #define DENALI_PI_181 U(0x22D4) 79478af8d3SJacky Bai #define DENALI_PI_182 U(0x22D8) 80478af8d3SJacky Bai #define DENALI_PI_191 U(0x22FC) 81478af8d3SJacky Bai #define DENALI_PI_192 U(0x2300) 82478af8d3SJacky Bai #define DENALI_PI_212 U(0x2350) 83478af8d3SJacky Bai #define DENALI_PI_214 U(0x2358) 84478af8d3SJacky Bai #define DENALI_PI_217 U(0x2364) 85478af8d3SJacky Bai 86478af8d3SJacky Bai #define LPDDR3_TYPE U(0x7) 87478af8d3SJacky Bai #define LPDDR4_TYPE U(0xB) 88478af8d3SJacky Bai 89478af8d3SJacky Bai extern void upower_wait_resp(void); 90478af8d3SJacky Bai 91478af8d3SJacky Bai struct dram_cfg_param { 92478af8d3SJacky Bai uint32_t reg; 93478af8d3SJacky Bai uint32_t val; 94478af8d3SJacky Bai }; 95478af8d3SJacky Bai 96478af8d3SJacky Bai struct dram_timing_info { 97478af8d3SJacky Bai /* ddr controller config */ 98478af8d3SJacky Bai struct dram_cfg_param *ctl_cfg; 99478af8d3SJacky Bai unsigned int ctl_cfg_num; 100478af8d3SJacky Bai /* pi config */ 101478af8d3SJacky Bai struct dram_cfg_param *pi_cfg; 102478af8d3SJacky Bai unsigned int pi_cfg_num; 103478af8d3SJacky Bai /* phy freq1 config */ 104478af8d3SJacky Bai struct dram_cfg_param *phy_f1_cfg; 105478af8d3SJacky Bai unsigned int phy_f1_cfg_num; 106478af8d3SJacky Bai /* phy freq2 config */ 107478af8d3SJacky Bai struct dram_cfg_param *phy_f2_cfg; 108478af8d3SJacky Bai unsigned int phy_f2_cfg_num; 109478af8d3SJacky Bai /* initialized drate table */ 110478af8d3SJacky Bai unsigned int fsp_table[3]; 111478af8d3SJacky Bai }; 112478af8d3SJacky Bai 113478af8d3SJacky Bai #define CTL_NUM U(680) 114478af8d3SJacky Bai #define PI_NUM U(298) 115478af8d3SJacky Bai #define PHY_NUM U(1654) 116478af8d3SJacky Bai #define PHY_DIFF_NUM U(49) 117478af8d3SJacky Bai struct dram_cfg { 118478af8d3SJacky Bai uint32_t ctl_cfg[CTL_NUM]; 119478af8d3SJacky Bai uint32_t pi_cfg[PI_NUM]; 120478af8d3SJacky Bai uint32_t phy_full[PHY_NUM]; 121478af8d3SJacky Bai uint32_t phy_diff[PHY_DIFF_NUM]; 122478af8d3SJacky Bai }; 123478af8d3SJacky Bai 124478af8d3SJacky Bai struct dram_timing_info *info; 125478af8d3SJacky Bai struct dram_cfg *dram_timing_cfg; 126478af8d3SJacky Bai 127478af8d3SJacky Bai /* mark if dram cfg is already saved */ 128478af8d3SJacky Bai static bool dram_cfg_saved; 129478af8d3SJacky Bai static uint32_t dram_class; 130478af8d3SJacky Bai 131478af8d3SJacky Bai /* PHY register index for frequency diff */ 132478af8d3SJacky Bai uint32_t freq_specific_reg_array[PHY_DIFF_NUM] = { 133478af8d3SJacky Bai 90, 92, 93, 96, 97, 100, 101, 102, 103, 104, 114, 134478af8d3SJacky Bai 346, 348, 349, 352, 353, 356, 357, 358, 359, 360, 135478af8d3SJacky Bai 370, 602, 604, 605, 608, 609, 612, 613, 614, 615, 136478af8d3SJacky Bai 616, 626, 858, 860, 861, 864, 865, 868, 869, 870, 137478af8d3SJacky Bai 871, 872, 882, 1063, 1319, 1566, 1624, 1625 138478af8d3SJacky Bai }; 139478af8d3SJacky Bai 140caee2733SJacky Bai /* lock used for DDR DVFS */ 141caee2733SJacky Bai spinlock_t dfs_lock; 142caee2733SJacky Bai static volatile uint32_t core_count; 143caee2733SJacky Bai static volatile bool in_progress; 144*416c4433SJacky Bai static volatile bool sys_dvfs; 145caee2733SJacky Bai static int num_fsp; 146caee2733SJacky Bai 147478af8d3SJacky Bai static void ddr_init(void) 148478af8d3SJacky Bai { 149478af8d3SJacky Bai unsigned int i; 150478af8d3SJacky Bai 151478af8d3SJacky Bai /* restore the ddr ctl config */ 152478af8d3SJacky Bai for (i = 0U; i < CTL_NUM; i++) { 153478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + i * 4, dram_timing_cfg->ctl_cfg[i]); 154478af8d3SJacky Bai } 155478af8d3SJacky Bai 156478af8d3SJacky Bai /* load the PI registers */ 157478af8d3SJacky Bai for (i = 0U; i < PI_NUM; i++) { 158478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + 0x2000 + i * 4, dram_timing_cfg->pi_cfg[i]); 159478af8d3SJacky Bai } 160478af8d3SJacky Bai 161478af8d3SJacky Bai 162478af8d3SJacky Bai /* restore all PHY registers for all the fsp. */ 163478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x100); 164478af8d3SJacky Bai /* restore all the phy configs */ 165478af8d3SJacky Bai for (i = 0U; i < PHY_NUM; i++) { 166478af8d3SJacky Bai /* skip the reserved registers space */ 167478af8d3SJacky Bai if (i >= 121U && i <= 255U) { 168478af8d3SJacky Bai continue; 169478af8d3SJacky Bai } 170478af8d3SJacky Bai if (i >= 377U && i <= 511U) { 171478af8d3SJacky Bai continue; 172478af8d3SJacky Bai } 173478af8d3SJacky Bai if (i >= 633U && i <= 767U) { 174478af8d3SJacky Bai continue; 175478af8d3SJacky Bai } 176478af8d3SJacky Bai if (i >= 889U && i <= 1023U) { 177478af8d3SJacky Bai continue; 178478af8d3SJacky Bai } 179478af8d3SJacky Bai if (i >= 1065U && i <= 1279U) { 180478af8d3SJacky Bai continue; 181478af8d3SJacky Bai } 182478af8d3SJacky Bai if (i >= 1321U && i <= 1535U) { 183478af8d3SJacky Bai continue; 184478af8d3SJacky Bai } 185478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + 0x4000 + i * 4, dram_timing_cfg->phy_full[i]); 186478af8d3SJacky Bai } 187478af8d3SJacky Bai 188478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) { 189478af8d3SJacky Bai /* restore only the diff. */ 190478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x0); 191478af8d3SJacky Bai for (i = 0U; i < PHY_DIFF_NUM; i++) { 192478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + 0x4000 + freq_specific_reg_array[i] * 4, 193478af8d3SJacky Bai dram_timing_cfg->phy_diff[i]); 194478af8d3SJacky Bai } 195478af8d3SJacky Bai } 196478af8d3SJacky Bai 197478af8d3SJacky Bai /* Re-enable MULTICAST mode */ 198478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, PHY_FREQ_MULTICAST_EN(1)); 199478af8d3SJacky Bai } 200478af8d3SJacky Bai 201478af8d3SJacky Bai void dram_enter_retention(void) 202478af8d3SJacky Bai { 203478af8d3SJacky Bai unsigned int i; 204478af8d3SJacky Bai 205478af8d3SJacky Bai /* 1. config the PCC_LPDDR4[SSADO] to 2b'11 for ACK domain 0/1's STOP */ 206478af8d3SJacky Bai mmio_setbits_32(IMX_PCC5_BASE + 0x108, 0x2 << 22); 207478af8d3SJacky Bai 208478af8d3SJacky Bai /* 209478af8d3SJacky Bai * 2. Make sure the DENALI_CTL_144[LPI_WAKEUP_EN[5:0]] has the bit 210478af8d3SJacky Bai * LPI_WAKEUP_EN[3] = 1b'1. This enables the option 'self-refresh 211478af8d3SJacky Bai * long with mem and ctlr clk gating or self-refresh power-down 212478af8d3SJacky Bai * long with mem and ctlr clk gating' 213478af8d3SJacky Bai */ 214478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, BIT(3) << LPI_WAKEUP_EN_SHIFT); 215478af8d3SJacky Bai 216478af8d3SJacky Bai /* 217478af8d3SJacky Bai * 3a. Config SIM_LPAV LPDDR_CTRL[LPDDR_AUTO_LP_MODE_DISABLE] to 1b'0(enable 218478af8d3SJacky Bai * the logic to automatic handles low power entry/exit. This is the recommended 219478af8d3SJacky Bai * option over handling through software. 220478af8d3SJacky Bai * 3b. Config the SIM_LPAV LPDDR_CTRL[SOC_LP_CMD] to 6b'101001(encoding for 221478af8d3SJacky Bai * self_refresh with both DDR controller and DRAM clock gate. THis is mandatory 222478af8d3SJacky Bai * since LPPDR logic will be power gated). 223478af8d3SJacky Bai */ 224478af8d3SJacky Bai mmio_clrbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL, LPDDR_AUTO_LP_MODE_DISABLE); 225478af8d3SJacky Bai mmio_clrsetbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL, 226478af8d3SJacky Bai 0x3f << SOC_LP_CMD_SHIFT, 0x29 << SOC_LP_CMD_SHIFT); 227478af8d3SJacky Bai 228478af8d3SJacky Bai /* Save DDR Controller & PHY config. 229478af8d3SJacky Bai * Set PHY_FREQ_SEL_MULTICAST_EN=0 & PHY_FREQ_SEL_INDEX=1. Read and store all 230478af8d3SJacky Bai * the PHY registers for F2 into phy_f1_cfg, then read/store the diff between 231478af8d3SJacky Bai * F1 & F2 into phy_f2_cfg. 232478af8d3SJacky Bai */ 233478af8d3SJacky Bai if (!dram_cfg_saved) { 234478af8d3SJacky Bai info = (struct dram_timing_info *)SAVED_DRAM_DATA_BASE; 235478af8d3SJacky Bai dram_timing_cfg = (struct dram_cfg *)(SAVED_DRAM_DATA_BASE + 236478af8d3SJacky Bai sizeof(struct dram_timing_info)); 237478af8d3SJacky Bai 238478af8d3SJacky Bai /* get the dram type */ 239478af8d3SJacky Bai dram_class = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_00); 240478af8d3SJacky Bai dram_class = (dram_class >> 8) & 0xf; 241478af8d3SJacky Bai 242478af8d3SJacky Bai /* save the ctl registers */ 243478af8d3SJacky Bai for (i = 0U; i < CTL_NUM; i++) { 244478af8d3SJacky Bai dram_timing_cfg->ctl_cfg[i] = mmio_read_32(IMX_DDRC_BASE + i * 4); 245478af8d3SJacky Bai } 246478af8d3SJacky Bai dram_timing_cfg->ctl_cfg[0] = dram_timing_cfg->ctl_cfg[0] & 0xFFFFFFFE; 247478af8d3SJacky Bai 248478af8d3SJacky Bai /* save the PI registers */ 249478af8d3SJacky Bai for (i = 0U; i < PI_NUM; i++) { 250478af8d3SJacky Bai dram_timing_cfg->pi_cfg[i] = mmio_read_32(IMX_DDRC_BASE + 0x2000 + i * 4); 251478af8d3SJacky Bai } 252478af8d3SJacky Bai dram_timing_cfg->pi_cfg[0] = dram_timing_cfg->pi_cfg[0] & 0xFFFFFFFE; 253478af8d3SJacky Bai 254478af8d3SJacky Bai /* 255478af8d3SJacky Bai * Read and store all PHY registers. full array is a full 256478af8d3SJacky Bai * copy for all the setpoint 257478af8d3SJacky Bai */ 258478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) { 259478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x10000); 260478af8d3SJacky Bai for (i = 0U; i < PHY_NUM; i++) { 261478af8d3SJacky Bai /* Make sure MULTICASE is enabled */ 262478af8d3SJacky Bai if (i == 1537U) { 263478af8d3SJacky Bai dram_timing_cfg->phy_full[i] = 0x100; 264478af8d3SJacky Bai } else { 265478af8d3SJacky Bai dram_timing_cfg->phy_full[i] = mmio_read_32(IMX_DDRC_BASE + 0x4000 + i * 4); 266478af8d3SJacky Bai } 267478af8d3SJacky Bai } 268478af8d3SJacky Bai 269478af8d3SJacky Bai /* 270478af8d3SJacky Bai * set PHY_FREQ_SEL_MULTICAST_EN=0 & PHY_FREQ_SEL_INDEX=0. 271478af8d3SJacky Bai * Read and store only the diff. 272478af8d3SJacky Bai */ 273478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x0); 274478af8d3SJacky Bai /* save only the frequency based diff config to save memory */ 275478af8d3SJacky Bai for (i = 0U; i < PHY_DIFF_NUM; i++) { 276478af8d3SJacky Bai dram_timing_cfg->phy_diff[i] = mmio_read_32(IMX_DDRC_BASE + 0x4000 + 277478af8d3SJacky Bai freq_specific_reg_array[i] * 4); 278478af8d3SJacky Bai } 279478af8d3SJacky Bai } else { 280478af8d3SJacky Bai /* LPDDR3, only f1 need to save */ 281478af8d3SJacky Bai for (i = 0U; i < info->phy_f1_cfg_num; i++) { 282478af8d3SJacky Bai info->phy_f1_cfg[i].val = mmio_read_32(info->phy_f1_cfg[i].reg); 283478af8d3SJacky Bai } 284478af8d3SJacky Bai } 285478af8d3SJacky Bai 286478af8d3SJacky Bai dram_cfg_saved = true; 287478af8d3SJacky Bai } 288478af8d3SJacky Bai } 289478af8d3SJacky Bai 290478af8d3SJacky Bai void dram_exit_retention(void) 291478af8d3SJacky Bai { 292478af8d3SJacky Bai uint32_t val; 293478af8d3SJacky Bai 294478af8d3SJacky Bai /* 1. Config the LPAV PLL4 and DDR clock for the desired LPDDR operating frequency. */ 295478af8d3SJacky Bai mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(30)); 296478af8d3SJacky Bai 297478af8d3SJacky Bai /* 2. Write PCC5.PCC_LPDDR4[SWRST] to 1b'1 to release LPDDR from reset. */ 298478af8d3SJacky Bai mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(28)); 299478af8d3SJacky Bai 300478af8d3SJacky Bai /* 3. Reload the LPDDR CTL/PI/PHY register */ 301478af8d3SJacky Bai ddr_init(); 302478af8d3SJacky Bai 303478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) { 304478af8d3SJacky Bai /* 4a. FIXME Set PHY_SET_DFI_INPUT_N parameters to 4'h1. LPDDR4 only */ 305478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1559, 0x01010101); 306478af8d3SJacky Bai 307478af8d3SJacky Bai /* 308478af8d3SJacky Bai * 4b. CTL PWRUP_SREFRESH_EXIT=1'b0 for disabling self refresh exit 309478af8d3SJacky Bai * from controller. 310478af8d3SJacky Bai */ 311478af8d3SJacky Bai /* 312478af8d3SJacky Bai * 4c. PI_PWRUP_SELF_REF_EXIT=1, PI_MC_PWRUP_SELF_REF_EXIT=0 for enabling 313478af8d3SJacky Bai * self refresh exit from PI 314478af8d3SJacky Bai */ 315478af8d3SJacky Bai /* 4c. PI_INT_LVL_EN=0 to skip Initialization trainings. */ 316478af8d3SJacky Bai /* 317478af8d3SJacky Bai * 4d. PI_WRLVL_EN_F0/1/2= PI_CALVL_EN_F0/1/2= PI_RDLVL_EN_F0/1/2= 318478af8d3SJacky Bai * PI_RDLVL_GATE_EN_F0/1/2= PI_WDQLVL_EN_F0/1/2=0x2. 319478af8d3SJacky Bai * Enable non initialization trainings. 320478af8d3SJacky Bai */ 321478af8d3SJacky Bai /* 4e. PI_PWRUP_SREFRESH_EXIT_CS=0xF */ 322478af8d3SJacky Bai /* 4f. PI_DLL_RESET=0x1 */ 323478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_137, 0x1); 324478af8d3SJacky Bai /* PI_PWRUP_SELF_REF_EXIT = 1 */ 325478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_132, 0x01000000); 326478af8d3SJacky Bai /* PI_MC_PWRUP_SELF_REF_EXIT = 0 */ 327478af8d3SJacky Bai mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_132, BIT(16)); 328478af8d3SJacky Bai /* PI_INT_LVL_EN = 0 */ 329478af8d3SJacky Bai mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_04, BIT(0)); 330478af8d3SJacky Bai /* PI_WRLVL_EN_F0 = 3, PI_WRLVL_EN_F1 = 3 */ 331478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_174, 0x03030000); 332478af8d3SJacky Bai /* PI_WRLVL_EN_F2 = 3 */ 333478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_175, 0x03); 334478af8d3SJacky Bai /* PI_CALVL_EN_F0 = 3, PI_CALVL_EN_F1 = 3 */ 335478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_191, 0x03030000); 336478af8d3SJacky Bai /* PI_CALVL_EN_F2 = 3 */ 337478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_192, 0x03); 338478af8d3SJacky Bai /* PI_WDQLVL_EN_F0 = 3 */ 339478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_212, 0x300); 340478af8d3SJacky Bai /* PI_WDQLVL_EN_F1 = 3 */ 341478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_214, 0x03000000); 342478af8d3SJacky Bai /* PI_WDQLVL_EN_F2 = 3 */ 343478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_217, 0x300); 344478af8d3SJacky Bai /* PI_EDLVL_EN_F0 = 3, PI_EDLVL_GATE_EN_F0 = 3 */ 345478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_181, 0x03030000); 346478af8d3SJacky Bai /* 347478af8d3SJacky Bai * PI_RDLVL_EN_F1 = 3, PI_RDLVL_GATE_EN_F1 = 3, 348478af8d3SJacky Bai * PI_RDLVL_EN_F2 = 3, PI_RDLVL_GATE_EN_F2 = 3 349478af8d3SJacky Bai */ 350478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_182, 0x03030303); 351478af8d3SJacky Bai /* PI_PWRUP_SREFRESH_EXIT_CS = 0xF */ 352478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_134, 0x000F0000); 353478af8d3SJacky Bai } else { 354478af8d3SJacky Bai /* PI_DLL_RESET=1 */ 355478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_137, 0x1); 356478af8d3SJacky Bai /* PI_PWRUP_SELF_REF_EXIT=1 */ 357478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_132, 0x01000000); 358478af8d3SJacky Bai /* PI_MC_PWRUP_SELF_REF_EXIT=0 */ 359478af8d3SJacky Bai mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_132, BIT(16)); 360478af8d3SJacky Bai /* PI_INT_LVL_EN=0 */ 361478af8d3SJacky Bai mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_04, BIT(0)); 362478af8d3SJacky Bai /* PI_WRLVL_EN_F0=3 */ 363478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_174, 0x00030000); 364478af8d3SJacky Bai /* PI_CALVL_EN_F0=3 */ 365478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_191, 0x00030000); 366478af8d3SJacky Bai /* PI_RDLVL_EN_F0=3,PI_RDLVL_GATE_EN_F0=3 */ 367478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_181, 0x03030000); 368478af8d3SJacky Bai /* PI_PWRUP_SREFRESH_EXIT_CS=0xF */ 369478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_134, 0x000F0000); 370478af8d3SJacky Bai } 371478af8d3SJacky Bai 372478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, 0x00002D00); 373478af8d3SJacky Bai 374478af8d3SJacky Bai /* Force in-order AXI read data */ 375478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, 0x1); 376478af8d3SJacky Bai 377478af8d3SJacky Bai /* 378478af8d3SJacky Bai * Disable special R/W group switches so that R/W group placement 379478af8d3SJacky Bai * is always at END of R/W group. 380478af8d3SJacky Bai */ 381478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_249, 0x0); 382478af8d3SJacky Bai 383478af8d3SJacky Bai /* Reduce time for IO pad calibration */ 384478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1590, 0x01000000); 385478af8d3SJacky Bai 386478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_25, 0x00020100); 387478af8d3SJacky Bai 388478af8d3SJacky Bai /* PD disable */ 389478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_153, 0x04040000); 390478af8d3SJacky Bai /* 391478af8d3SJacky Bai * 5. Disable automatic LP entry and PCPCS modes LP_AUTO_ENTRY_EN 392478af8d3SJacky Bai * to 1b'0, PCPCS_PD_EN to 1b'0 393478af8d3SJacky Bai */ 394478af8d3SJacky Bai 395478af8d3SJacky Bai upwr_xcp_set_ddr_retention(APD_DOMAIN, 0, NULL); 396478af8d3SJacky Bai upower_wait_resp(); 397478af8d3SJacky Bai 398478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) { 399478af8d3SJacky Bai /* 7. Write PI START parameter to 1'b1 */ 400478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PI_00, 0x00000b01); 401478af8d3SJacky Bai 402478af8d3SJacky Bai /* 8. Write CTL START parameter to 1'b1 */ 403478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_00, 0x00000b01); 404478af8d3SJacky Bai } else { 405478af8d3SJacky Bai /* 7. Write PI START parameter to 1'b1 */ 406478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PI_00, 0x00000701); 407478af8d3SJacky Bai 408478af8d3SJacky Bai /* 8. Write CTL START parameter to 1'b1 */ 409478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_00, 0x00000701); 410478af8d3SJacky Bai } 411478af8d3SJacky Bai 412478af8d3SJacky Bai /* 9. DENALI_CTL_266: Wait for INT_STATUS_INIT=0x2 */ 413478af8d3SJacky Bai do { 414478af8d3SJacky Bai val = (mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_266) >> 8) & 0xFF; 415478af8d3SJacky Bai } while (val != 0x2); 416478af8d3SJacky Bai 417478af8d3SJacky Bai /* 418478af8d3SJacky Bai * 10. Run SW trainings by setting PI_CALVL_REQ,PI_WRLVL_REQ,PI_RDLVL_GATE_REQ, 419478af8d3SJacky Bai * PI_RDLVL_REQ,PI_WDQLVL_REQ(NA for LPDDR3) in same order. 420478af8d3SJacky Bai */ 421478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) { 422478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_52, 0x10000); /* CALVL */ 423478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_26, 0x100); /* WRLVL */ 424478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x10000); /* RDGATE */ 425478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x100); /* RDQLVL */ 426478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_65, 0x10000); /* WDQLVL */ 427478af8d3SJacky Bai 428478af8d3SJacky Bai /* 11. Wait for trainings to get complete by polling PI_INT_STATUS */ 429478af8d3SJacky Bai while ((mmio_read_32(IMX_DDRC_BASE + DENALI_PI_77) & 0x07E00000) != 0x07E00000) { 430478af8d3SJacky Bai ; 431478af8d3SJacky Bai } 432478af8d3SJacky Bai } else { 433478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_52, 0x10000); /* CALVL */ 434478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_26, 0x100); /* WRLVL */ 435478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x10000); /* RDGATE */ 436478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x100); /* RDQLVL */ 437478af8d3SJacky Bai while ((mmio_read_32(IMX_DDRC_BASE + DENALI_PI_77) & 0x05E00000) != 0x05E00000) { 438478af8d3SJacky Bai ; 439478af8d3SJacky Bai } 440478af8d3SJacky Bai } 441478af8d3SJacky Bai } 442caee2733SJacky Bai 443caee2733SJacky Bai #define LPDDR_DONE (0x1<<4) 444caee2733SJacky Bai #define SOC_FREQ_CHG_ACK (0x1<<6) 445caee2733SJacky Bai #define SOC_FREQ_CHG_REQ (0x1<<7) 446caee2733SJacky Bai #define LPI_WAKEUP_EN (0x4<<8) 447caee2733SJacky Bai #define SOC_FREQ_REQ (0x1<<11) 448caee2733SJacky Bai 449caee2733SJacky Bai #define LPDDR_EN_CLKGATE (0x1<<17) 450caee2733SJacky Bai 451caee2733SJacky Bai static void set_cgc2_ddrclk(uint8_t src, uint8_t div) 452caee2733SJacky Bai { 453caee2733SJacky Bai 454caee2733SJacky Bai /* Wait until the reg is unlocked for writing */ 455caee2733SJacky Bai while (mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(31)) 456caee2733SJacky Bai ; 457caee2733SJacky Bai 458caee2733SJacky Bai mmio_write_32(IMX_CGC2_BASE + 0x40, (src << 28) | (div << 21)); 459caee2733SJacky Bai /* Wait for the clock switching done */ 460caee2733SJacky Bai while (!(mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(27))) 461caee2733SJacky Bai ; 462caee2733SJacky Bai } 463caee2733SJacky Bai static void set_ddr_clk(uint32_t ddr_freq) 464caee2733SJacky Bai { 465caee2733SJacky Bai /* Disable DDR clock */ 466caee2733SJacky Bai mmio_clrbits_32(IMX_PCC5_BASE + 0x108, BIT(30)); 467caee2733SJacky Bai switch (ddr_freq) { 468caee2733SJacky Bai /* boot frequency ? */ 469caee2733SJacky Bai case 48: 470caee2733SJacky Bai set_cgc2_ddrclk(2, 0); 471caee2733SJacky Bai break; 472caee2733SJacky Bai /* default bypass frequency for fsp 1 */ 473caee2733SJacky Bai case 192: 474caee2733SJacky Bai set_cgc2_ddrclk(0, 1); 475caee2733SJacky Bai break; 476caee2733SJacky Bai case 384: 477caee2733SJacky Bai set_cgc2_ddrclk(0, 0); 478caee2733SJacky Bai break; 479caee2733SJacky Bai case 264: 480caee2733SJacky Bai set_cgc2_ddrclk(4, 3); 481caee2733SJacky Bai break; 482caee2733SJacky Bai case 528: 483caee2733SJacky Bai set_cgc2_ddrclk(4, 1); 484caee2733SJacky Bai break; 485caee2733SJacky Bai default: 486caee2733SJacky Bai break; 487caee2733SJacky Bai } 488caee2733SJacky Bai /* Enable DDR clock */ 489caee2733SJacky Bai mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(30)); 490caee2733SJacky Bai 491caee2733SJacky Bai /* Wait until the reg is unlocked for writing */ 492caee2733SJacky Bai while (mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(31)) { 493caee2733SJacky Bai ; 494caee2733SJacky Bai } 495caee2733SJacky Bai } 496caee2733SJacky Bai 497caee2733SJacky Bai #define AVD_SIM_LPDDR_CTRL (IMX_LPAV_SIM_BASE + 0x14) 498caee2733SJacky Bai #define AVD_SIM_LPDDR_CTRL2 (IMX_LPAV_SIM_BASE + 0x18) 499caee2733SJacky Bai #define MAX_FSP_NUM U(3) 500caee2733SJacky Bai #define DDR_DFS_GET_FSP_COUNT 0x10 501caee2733SJacky Bai #define DDR_BYPASS_DRATE U(400) 502caee2733SJacky Bai 503*416c4433SJacky Bai extern int upower_pmic_i2c_write(uint32_t reg_addr, uint32_t reg_val); 504*416c4433SJacky Bai 505caee2733SJacky Bai /* Normally, we only switch frequency between 1(bypass) and 2(highest) */ 506caee2733SJacky Bai int lpddr4_dfs(uint32_t freq_index) 507caee2733SJacky Bai { 508caee2733SJacky Bai uint32_t lpddr_ctrl, lpddr_ctrl2; 509caee2733SJacky Bai uint32_t ddr_ctl_144; 510caee2733SJacky Bai 511caee2733SJacky Bai /* 512caee2733SJacky Bai * Valid index: 0 to 2 513caee2733SJacky Bai * index 0: boot frequency 514caee2733SJacky Bai * index 1: bypass frequency 515caee2733SJacky Bai * index 2: highest frequency 516caee2733SJacky Bai */ 517caee2733SJacky Bai if (freq_index > 2U) { 518caee2733SJacky Bai return -1; 519caee2733SJacky Bai } 520caee2733SJacky Bai 521*416c4433SJacky Bai /* 522*416c4433SJacky Bai * increase the voltage to 1.1V firstly before increase frequency 523*416c4433SJacky Bai * and APD enter OD mode 524*416c4433SJacky Bai */ 525*416c4433SJacky Bai if (freq_index == 2U && sys_dvfs) { 526*416c4433SJacky Bai upower_pmic_i2c_write(0x22, 0x28); 527*416c4433SJacky Bai } 528*416c4433SJacky Bai 529caee2733SJacky Bai /* Enable LPI_WAKEUP_EN */ 530caee2733SJacky Bai ddr_ctl_144 = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_144); 531caee2733SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, LPI_WAKEUP_EN); 532caee2733SJacky Bai 533caee2733SJacky Bai /* put DRAM into long self-refresh & clock gating */ 534caee2733SJacky Bai lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL); 535caee2733SJacky Bai lpddr_ctrl = (lpddr_ctrl & ~((0x3f << 15) | (0x3 << 9))) | (0x28 << 15) | (freq_index << 9); 536caee2733SJacky Bai mmio_write_32(AVD_SIM_LPDDR_CTRL, lpddr_ctrl); 537caee2733SJacky Bai 538caee2733SJacky Bai /* Gating the clock */ 539caee2733SJacky Bai lpddr_ctrl2 = mmio_read_32(AVD_SIM_LPDDR_CTRL2); 540caee2733SJacky Bai mmio_setbits_32(AVD_SIM_LPDDR_CTRL2, LPDDR_EN_CLKGATE); 541caee2733SJacky Bai 542caee2733SJacky Bai /* Request frequency change */ 543caee2733SJacky Bai mmio_setbits_32(AVD_SIM_LPDDR_CTRL, SOC_FREQ_REQ); 544caee2733SJacky Bai 545caee2733SJacky Bai do { 546caee2733SJacky Bai lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL); 547caee2733SJacky Bai if (lpddr_ctrl & SOC_FREQ_CHG_REQ) { 548caee2733SJacky Bai /* Bypass mode */ 549caee2733SJacky Bai if (info->fsp_table[freq_index] < DDR_BYPASS_DRATE) { 550caee2733SJacky Bai /* Change to PLL bypass mode */ 551caee2733SJacky Bai mmio_write_32(IMX_LPAV_SIM_BASE, 0x1); 552caee2733SJacky Bai /* change the ddr clock source & frequency */ 553caee2733SJacky Bai set_ddr_clk(info->fsp_table[freq_index]); 554caee2733SJacky Bai } else { 555caee2733SJacky Bai /* Change to PLL unbypass mode */ 556caee2733SJacky Bai mmio_write_32(IMX_LPAV_SIM_BASE, 0x0); 557caee2733SJacky Bai /* change the ddr clock source & frequency */ 558caee2733SJacky Bai set_ddr_clk(info->fsp_table[freq_index] >> 1); 559caee2733SJacky Bai } 560caee2733SJacky Bai 561caee2733SJacky Bai mmio_clrsetbits_32(AVD_SIM_LPDDR_CTRL, SOC_FREQ_CHG_REQ, SOC_FREQ_CHG_ACK); 562caee2733SJacky Bai continue; 563caee2733SJacky Bai } 564caee2733SJacky Bai } while ((lpddr_ctrl & LPDDR_DONE) != 0); /* several try? */ 565caee2733SJacky Bai 566caee2733SJacky Bai /* restore the original setting */ 567caee2733SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, ddr_ctl_144); 568caee2733SJacky Bai mmio_write_32(AVD_SIM_LPDDR_CTRL2, lpddr_ctrl2); 569caee2733SJacky Bai 570caee2733SJacky Bai /* Check the DFS result */ 571caee2733SJacky Bai lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL) & 0xF; 572caee2733SJacky Bai if (lpddr_ctrl != 0U) { 573caee2733SJacky Bai /* Must be something wrong, return failure */ 574caee2733SJacky Bai return -1; 575caee2733SJacky Bai } 576caee2733SJacky Bai 577*416c4433SJacky Bai /* decrease the BUCK3 voltage after frequency changed to lower 578*416c4433SJacky Bai * and APD in ND_MODE 579*416c4433SJacky Bai */ 580*416c4433SJacky Bai if (freq_index == 1U && sys_dvfs) { 581*416c4433SJacky Bai upower_pmic_i2c_write(0x22, 0x20); 582*416c4433SJacky Bai } 583*416c4433SJacky Bai 584caee2733SJacky Bai /* DFS done successfully */ 585caee2733SJacky Bai return 0; 586caee2733SJacky Bai } 587caee2733SJacky Bai 588caee2733SJacky Bai /* for the non-primary core, waiting for DFS done */ 589caee2733SJacky Bai static uint64_t waiting_dvfs(uint32_t id, uint32_t flags, 590caee2733SJacky Bai void *handle, void *cookie) 591caee2733SJacky Bai { 592caee2733SJacky Bai uint32_t irq; 593caee2733SJacky Bai 594caee2733SJacky Bai irq = plat_ic_acknowledge_interrupt(); 595caee2733SJacky Bai if (irq < 1022U) { 596caee2733SJacky Bai plat_ic_end_of_interrupt(irq); 597caee2733SJacky Bai } 598caee2733SJacky Bai 599caee2733SJacky Bai /* set the WFE done status */ 600caee2733SJacky Bai spin_lock(&dfs_lock); 601caee2733SJacky Bai core_count++; 602caee2733SJacky Bai dsb(); 603caee2733SJacky Bai spin_unlock(&dfs_lock); 604caee2733SJacky Bai 605caee2733SJacky Bai while (in_progress) { 606caee2733SJacky Bai wfe(); 607caee2733SJacky Bai } 608caee2733SJacky Bai 609caee2733SJacky Bai return 0; 610caee2733SJacky Bai } 611caee2733SJacky Bai 612caee2733SJacky Bai int dram_dvfs_handler(uint32_t smc_fid, void *handle, 613caee2733SJacky Bai u_register_t x1, u_register_t x2, u_register_t x3) 614caee2733SJacky Bai { 615caee2733SJacky Bai unsigned int fsp_index = x1; 616caee2733SJacky Bai uint32_t online_cpus = x2 - 1; 617caee2733SJacky Bai uint64_t mpidr = read_mpidr_el1(); 618caee2733SJacky Bai unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr); 619caee2733SJacky Bai 620caee2733SJacky Bai /* Get the number of FSPs */ 621caee2733SJacky Bai if (x1 == DDR_DFS_GET_FSP_COUNT) { 622caee2733SJacky Bai SMC_RET2(handle, num_fsp, info->fsp_table[1]); 623caee2733SJacky Bai } 624caee2733SJacky Bai 625caee2733SJacky Bai /* start lpddr frequency scaling */ 626caee2733SJacky Bai in_progress = true; 627*416c4433SJacky Bai sys_dvfs = x3 ? true : false; 628caee2733SJacky Bai dsb(); 629caee2733SJacky Bai 630caee2733SJacky Bai /* notify other core wait for scaling done */ 631caee2733SJacky Bai for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; i++) 632caee2733SJacky Bai /* Skip raise SGI for current CPU */ 633caee2733SJacky Bai if (i != cpu_id) { 634caee2733SJacky Bai plat_ic_raise_el3_sgi(0x8, i); 635caee2733SJacky Bai } 636caee2733SJacky Bai 637caee2733SJacky Bai /* Make sure all the cpu in WFE */ 638caee2733SJacky Bai while (online_cpus != core_count) { 639caee2733SJacky Bai ; 640caee2733SJacky Bai } 641caee2733SJacky Bai 642caee2733SJacky Bai /* Flush the L1/L2 cache */ 643caee2733SJacky Bai dcsw_op_all(DCCSW); 644caee2733SJacky Bai 645caee2733SJacky Bai lpddr4_dfs(fsp_index); 646caee2733SJacky Bai 647caee2733SJacky Bai in_progress = false; 648caee2733SJacky Bai core_count = 0; 649caee2733SJacky Bai dsb(); 650caee2733SJacky Bai sev(); 651caee2733SJacky Bai isb(); 652caee2733SJacky Bai 653caee2733SJacky Bai SMC_RET1(handle, 0); 654caee2733SJacky Bai } 655caee2733SJacky Bai 656caee2733SJacky Bai void dram_init(void) 657caee2733SJacky Bai { 658caee2733SJacky Bai uint32_t flags = 0; 659caee2733SJacky Bai uint32_t rc; 660caee2733SJacky Bai unsigned int i; 661caee2733SJacky Bai 662caee2733SJacky Bai /* Register the EL3 handler for DDR DVFS */ 663caee2733SJacky Bai set_interrupt_rm_flag(flags, NON_SECURE); 664caee2733SJacky Bai rc = register_interrupt_type_handler(INTR_TYPE_EL3, waiting_dvfs, flags); 665caee2733SJacky Bai if (rc) { 666caee2733SJacky Bai panic(); 667caee2733SJacky Bai } 668caee2733SJacky Bai 669caee2733SJacky Bai info = (struct dram_timing_info *)SAVED_DRAM_DATA_BASE; 670caee2733SJacky Bai 671caee2733SJacky Bai /* Get the num of the supported Fsp */ 672caee2733SJacky Bai for (i = 0; i < MAX_FSP_NUM; i++) { 673caee2733SJacky Bai if (!info->fsp_table[i]) { 674caee2733SJacky Bai break; 675caee2733SJacky Bai } 676caee2733SJacky Bai } 677caee2733SJacky Bai 678caee2733SJacky Bai num_fsp = (i > MAX_FSP_NUM) ? MAX_FSP_NUM : i; 679caee2733SJacky Bai } 680