summaryrefslogtreecommitdiff
path: root/plat/imx/imx8mq/ddr/lpddr4_hwffc.c
blob: cf46b7f911084ecac714f3cc0add98d131447cb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
 * 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");
}