| /* |
| * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. |
| * Copyright 2017-2018 NXP. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| void lpddr4_dvfs_hwffc(int init_vrcg, int init_fsp, int target_freq, |
| int discamdrain) |
| { |
| uint32_t tmp, tmp_t; |
| |
| INFO("hwffc enter\n"); |
| |
| /* step 1: hwffc flow enter, set the HWFCCCTL */ |
| tmp = ((init_fsp << 4) & 0x10) | ((init_vrcg << 5) & 0x20) | 0x40; |
| tmp |= 0x3; |
| mmio_write_32(IMX_DDRC_BASE + DDRC_HWFFCCTL, tmp); |
| |
| /* |
| * set SWCTL.sw_done to disable quasi-dynamic register programming |
| * outside reset |
| */ |
| mmio_write_32(IMX_DDRC_BASE + DDRC_SWCTL, 0x0); |
| |
| mmio_write_32(IMX_DDRC_BASE + DDRC_DFIMISC, 0x11); |
| |
| /* |
| * set SWCTL.sw_done to enable quasi-dynamic register programming |
| * outside reset. |
| */ |
| mmio_write_32(IMX_DDRC_BASE + DDRC_SWCTL, 0x1); |
| /*wait SWSTAT.sw_done_ack to 1 */ |
| while (!(mmio_read_32(IMX_DDRC_BASE + DDRC_SWSTAT) & 0x1)) |
| ; |
| |
| /* wait for DBGCAM is empty */ |
| if (discamdrain) |
| while ((mmio_read_32(IMX_DDRC_BASE + DDRC_DBGCAM) & |
| 0x30000000) != 0x30000000) |
| ; |
| |
| /* set HWFFC requirements, ddrc_csysfreqency_ddrc[1:0] = 2b'10; |
| * [15]: ccmsrcgpcmix_ddrc_csysdisdrain_ddrc; |
| * [14:13]: ccmsrcgpcmix_ddrc_csysfrequency_ddrc[1:0]; |
| * [12]: ccmsrcgpcmix_ddrc_csysmode_ddrc; |
| */ |
| tmp = mmio_read_32(0x303a0164); |
| tmp &= 0xFFFF0FFF; |
| tmp |= ((discamdrain << 15) & 0x8000); |
| tmp |= ((target_freq << 13) & 0x6000); |
| tmp |= 0x1000; |
| mmio_write_32(0x303a0164, tmp); |
| |
| /* set the gpc_ddr1_core_csysreq to active low */ |
| tmp = mmio_read_32(0x303a01fc); |
| tmp &= 0xfffffffe; |
| mmio_write_32(0x303a01fc, tmp); |
| |
| /* wait gpc_ddr1_core_csysack to active low */ |
| while ((mmio_read_32(0x303a01fc) & 0x10000)) |
| ; |
| |
| /* step B: switch the clock */ |
| if (target_freq == 0x1) { |
| /* change the clock source of dram_alt_clk_root to source 5 --400MHz */ |
| mmio_write_32(0x3038a008, (0x7 << 24) | (0x7 << 16)); |
| mmio_write_32(0x303aa004, (0x5 << 24)); |
| |
| /* change the clock source of dram_apb_clk_root to source 2 --40MHz/2 */ |
| mmio_write_32(0x3038a088, (0x7 << 24) | (0x7 << 16)); |
| mmio_write_32(0x3038a084, (0x2 << 24) | (0x1 << 16));; |
| |
| /* bypass the DRAM PLL */ |
| mmio_write_32(0x30389804, 1 << 24); |
| |
| } else if (target_freq == 0x2) { |
| /* change the clock source of dram_alt_clk_root to source 2 --100MHz */ |
| mmio_write_32(0x3038a008, (0x7 << 24) | (0x7 << 16)); |
| mmio_write_32(0x3038a004, (0x2 << 24)); |
| |
| /* change the clock source of dram_apb_clk_root to source 2 --40Mhz/2 */ |
| mmio_write_32(0x3038a088, (0x7 << 24) | (0x7 << 16)); |
| mmio_write_32(0x3038a084, (0x2 << 24) | (0x1 << 16));; |
| |
| /* bypass the DRAM PLL */ |
| mmio_write_32(0x30389804, 1 << 24); |
| } else { |
| /* switch to the default freq fsp = 0x1 3200mts */ |
| mmio_write_32(0x3038a088,(0x7<<24)|(0x7<<16)); |
| /* to source 4 --800MHz/ 4 */ |
| mmio_write_32(0x3038a084,(0x4<<24)|(0x3<<16)); |
| mmio_write_32(0x30389808, 1<<24); |
| } |
| |
| /* step C: exit hwffc flow */ |
| tmp = mmio_read_32(0x303a01fc); |
| tmp |= 0x1; |
| mmio_write_32(0x303a01fc, tmp); |
| /* wait gpc_ddr1_core_csysack to high */ |
| while (!(mmio_read_32(0x303a01fc) & 0x10000)) |
| ; |
| |
| /* [1:0]-->2b'10, to disable HWFFC */ |
| mmio_write_32(IMX_DDRC_BASE + DDRC_HWFFCCTL, 0x72); |
| |
| /*wait until hwffc_in_progress = 0 */ |
| while(mmio_read_32(IMX_DDRC_BASE + DDRC_HWFFCSTAT) & 0x1) |
| ; |
| |
| mmio_write_32(IMX_DDRC_BASE + DDRC_SWCTL, 0x0); |
| /* set MSTR.frequency_mode MSTR.[29], MSTR2.target_frequency[1:0] */ |
| tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_MSTR); |
| tmp |= 0x20000000; |
| mmio_write_32(IMX_DDRC_BASE + DDRC_MSTR, tmp); |
| |
| tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_HWFFCSTAT); |
| tmp_t = (tmp >> 4) & 0x3; |
| tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_MSTR2); |
| tmp = (tmp & 0xfffffffc) | tmp_t; |
| mmio_write_32(IMX_DDRC_BASE + DDRC_MSTR2, tmp); |
| |
| mmio_write_32(IMX_DDRC_BASE + DDRC_SWCTL, 0x1); |
| mmio_write_32(IMX_DDRC_BASE + DDRC_HWFFCCTL, 0x70); |
| |
| /* why we need below flow */ |
| mmio_write_32(IMX_DDRC_BASE + DDRC_PWRCTL, 0x1a8); |
| while ((mmio_read_32(IMX_DDRC_BASE + DDRC_STAT) & 0x3) != 0x3) |
| ; |
| /* wait SDRAM into self refresh state */ |
| while(!(mmio_read_32(IMX_DDRC_BASE + DDRC_STAT) & 0x30)) |
| ; |
| |
| tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_CRCPARSTAT); |
| tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_CRCPARSTAT); |
| |
| mmio_write_32(IMX_DDRC_BASE + DDRC_PWRCTL, 0x188); |
| |
| /* wait STAT to normal mode */ |
| while ((mmio_read_32(IMX_DDRC_BASE + DDRC_STAT) & 0x3) != 0x1) |
| ; |
| |
| INFO("hwffc exit\n"); |
| } |