blob: a54449e5f14b5de4bc524ebeb249ee61eafcccdb [file] [log] [blame]
Peng Fan692f9432018-11-20 10:19:57 +00001// SPDX-License-Identifier: GPL-2.0+
2/*
Fabio Estevam47425ef2020-07-17 16:36:53 -03003 * Copyright 2018 NXP
4 */
Peng Fan692f9432018-11-20 10:19:57 +00005
6#include <common.h>
7#include <errno.h>
Simon Glass0f2af882020-05-10 11:40:05 -06008#include <log.h>
Peng Fan692f9432018-11-20 10:19:57 +00009#include <asm/io.h>
10#include <asm/arch/ddr.h>
11#include <asm/arch/clock.h>
12#include <asm/arch/ddr.h>
13#include <asm/arch/lpddr4_define.h>
Oliver Chen42eda3a2020-04-21 14:48:09 +080014#include <asm/arch/sys_proto.h>
15
16static unsigned int g_cdd_rr_max[4];
17static unsigned int g_cdd_rw_max[4];
18static unsigned int g_cdd_wr_max[4];
19static unsigned int g_cdd_ww_max[4];
Peng Fan692f9432018-11-20 10:19:57 +000020
21static inline void poll_pmu_message_ready(void)
22{
23 unsigned int reg;
24
25 do {
26 reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0004);
27 } while (reg & 0x1);
28}
29
30static inline void ack_pmu_message_receive(void)
31{
32 unsigned int reg;
33
34 reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0031, 0x0);
35
36 do {
37 reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0004);
38 } while (!(reg & 0x1));
39
40 reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0031, 0x1);
41}
42
43static inline unsigned int get_mail(void)
44{
45 unsigned int reg;
46
47 poll_pmu_message_ready();
48
49 reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0032);
50
51 ack_pmu_message_receive();
52
53 return reg;
54}
55
56static inline unsigned int get_stream_message(void)
57{
58 unsigned int reg, reg2;
59
60 poll_pmu_message_ready();
61
62 reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0032);
63
64 reg2 = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0034);
65
66 reg2 = (reg2 << 16) | reg;
67
68 ack_pmu_message_receive();
69
70 return reg2;
71}
72
73static inline void decode_major_message(unsigned int mail)
74{
75 debug("[PMU Major message = 0x%08x]\n", mail);
76}
77
78static inline void decode_streaming_message(void)
79{
80 unsigned int string_index, arg __maybe_unused;
81 int i = 0;
82
83 string_index = get_stream_message();
84 debug("PMU String index = 0x%08x\n", string_index);
85 while (i < (string_index & 0xffff)) {
86 arg = get_stream_message();
87 debug("arg[%d] = 0x%08x\n", i, arg);
88 i++;
89 }
90
91 debug("\n");
92}
93
Frieder Schrempf2a9b1f52019-12-11 10:01:19 +000094int wait_ddrphy_training_complete(void)
Peng Fan692f9432018-11-20 10:19:57 +000095{
96 unsigned int mail;
97
98 while (1) {
99 mail = get_mail();
100 decode_major_message(mail);
101 if (mail == 0x08) {
102 decode_streaming_message();
103 } else if (mail == 0x07) {
104 debug("Training PASS\n");
Frieder Schrempf2a9b1f52019-12-11 10:01:19 +0000105 return 0;
Peng Fan692f9432018-11-20 10:19:57 +0000106 } else if (mail == 0xff) {
Frieder Schrempf2a9b1f52019-12-11 10:01:19 +0000107 debug("Training FAILED\n");
108 return -1;
Peng Fan692f9432018-11-20 10:19:57 +0000109 }
110 }
111}
112
113void ddrphy_init_set_dfi_clk(unsigned int drate)
114{
115 switch (drate) {
Peng Fan49dbcee2019-12-30 09:58:52 +0800116 case 4000:
117 dram_pll_init(MHZ(1000));
118 dram_disable_bypass();
119 break;
Marek Vasutea0209f2022-02-26 04:37:42 +0100120 case 3732:
121 dram_pll_init(MHZ(933));
122 dram_disable_bypass();
123 break;
Peng Fan692f9432018-11-20 10:19:57 +0000124 case 3200:
125 dram_pll_init(MHZ(800));
126 dram_disable_bypass();
127 break;
128 case 3000:
129 dram_pll_init(MHZ(750));
130 dram_disable_bypass();
131 break;
132 case 2400:
133 dram_pll_init(MHZ(600));
134 dram_disable_bypass();
135 break;
136 case 1600:
137 dram_pll_init(MHZ(400));
138 dram_disable_bypass();
139 break;
Jacky Baid62ddc12019-08-08 09:59:08 +0000140 case 1066:
141 dram_pll_init(MHZ(266));
142 dram_disable_bypass();
143 break;
Peng Fan692f9432018-11-20 10:19:57 +0000144 case 667:
145 dram_pll_init(MHZ(167));
146 dram_disable_bypass();
147 break;
148 case 400:
149 dram_enable_bypass(MHZ(400));
150 break;
151 case 100:
152 dram_enable_bypass(MHZ(100));
153 break;
154 default:
155 return;
156 }
157}
158
159void ddrphy_init_read_msg_block(enum fw_type type)
160{
161}
162
163void lpddr4_mr_write(unsigned int mr_rank, unsigned int mr_addr,
164 unsigned int mr_data)
165{
166 unsigned int tmp;
167 /*
168 * 1. Poll MRSTAT.mr_wr_busy until it is 0.
169 * This checks that there is no outstanding MR transaction.
170 * No writes should be performed to MRCTRL0 and MRCTRL1 if
171 * MRSTAT.mr_wr_busy = 1.
172 */
173 do {
174 tmp = reg32_read(DDRC_MRSTAT(0));
175 } while (tmp & 0x1);
176 /*
177 * 2. Write the MRCTRL0.mr_type, MRCTRL0.mr_addr, MRCTRL0.mr_rank and
178 * (for MRWs) MRCTRL1.mr_data to define the MR transaction.
179 */
180 reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4));
181 reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8) | mr_data);
182 reg32setbit(DDRC_MRCTRL0(0), 31);
183}
184
185unsigned int lpddr4_mr_read(unsigned int mr_rank, unsigned int mr_addr)
186{
187 unsigned int tmp;
188
189 reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x1);
190 do {
191 tmp = reg32_read(DDRC_MRSTAT(0));
192 } while (tmp & 0x1);
193
194 reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4) | 0x1);
195 reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8));
196 reg32setbit(DDRC_MRCTRL0(0), 31);
197 do {
198 tmp = reg32_read(DRC_PERF_MON_MRR0_DAT(0));
199 } while ((tmp & 0x8) == 0);
200 tmp = reg32_read(DRC_PERF_MON_MRR1_DAT(0));
201 tmp = tmp & 0xff;
202 reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x4);
203
204 return tmp;
205}
Oliver Chen42eda3a2020-04-21 14:48:09 +0800206
207unsigned int look_for_max(unsigned int data[],
Fabio Estevam47425ef2020-07-17 16:36:53 -0300208 unsigned int addr_start, unsigned int addr_end)
Oliver Chen42eda3a2020-04-21 14:48:09 +0800209{
210 unsigned int i, imax = 0;
211
212 for (i = addr_start; i <= addr_end; i++) {
213 if (((data[i] >> 7) == 0) && (data[i] > imax))
214 imax = data[i];
215 }
216
217 return imax;
218}
219
220void get_trained_CDD(u32 fsp)
221{
222 unsigned int i, ddr_type, tmp;
223 unsigned int cdd_cha[12], cdd_chb[12];
224 unsigned int cdd_cha_rr_max, cdd_cha_rw_max, cdd_cha_wr_max, cdd_cha_ww_max;
225 unsigned int cdd_chb_rr_max, cdd_chb_rw_max, cdd_chb_wr_max, cdd_chb_ww_max;
226
227 ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
228 if (ddr_type == 0x20) {
229 for (i = 0; i < 6; i++) {
230 tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x54013 + i) * 4);
231 cdd_cha[i * 2] = tmp & 0xff;
232 cdd_cha[i * 2 + 1] = (tmp >> 8) & 0xff;
233 }
234
235 for (i = 0; i < 7; i++) {
236 tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x5402c + i) * 4);
237 if (i == 0) {
238 cdd_cha[0] = (tmp >> 8) & 0xff;
239 } else if (i == 6) {
Fabio Estevam47425ef2020-07-17 16:36:53 -0300240 cdd_cha[11] = tmp & 0xff;
Oliver Chen42eda3a2020-04-21 14:48:09 +0800241 } else {
Fabio Estevam47425ef2020-07-17 16:36:53 -0300242 cdd_chb[i * 2 - 1] = tmp & 0xff;
Oliver Chen42eda3a2020-04-21 14:48:09 +0800243 cdd_chb[i * 2] = (tmp >> 8) & 0xff;
244 }
245 }
246
247 cdd_cha_rr_max = look_for_max(cdd_cha, 0, 1);
248 cdd_cha_rw_max = look_for_max(cdd_cha, 2, 5);
249 cdd_cha_wr_max = look_for_max(cdd_cha, 6, 9);
250 cdd_cha_ww_max = look_for_max(cdd_cha, 10, 11);
251 cdd_chb_rr_max = look_for_max(cdd_chb, 0, 1);
252 cdd_chb_rw_max = look_for_max(cdd_chb, 2, 5);
253 cdd_chb_wr_max = look_for_max(cdd_chb, 6, 9);
254 cdd_chb_ww_max = look_for_max(cdd_chb, 10, 11);
255 g_cdd_rr_max[fsp] = cdd_cha_rr_max > cdd_chb_rr_max ? cdd_cha_rr_max : cdd_chb_rr_max;
256 g_cdd_rw_max[fsp] = cdd_cha_rw_max > cdd_chb_rw_max ? cdd_cha_rw_max : cdd_chb_rw_max;
257 g_cdd_wr_max[fsp] = cdd_cha_wr_max > cdd_chb_wr_max ? cdd_cha_wr_max : cdd_chb_wr_max;
258 g_cdd_ww_max[fsp] = cdd_cha_ww_max > cdd_chb_ww_max ? cdd_cha_ww_max : cdd_chb_ww_max;
259 } else {
260 unsigned int ddr4_cdd[64];
Fabio Estevam47425ef2020-07-17 16:36:53 -0300261
262 for (i = 0; i < 29; i++) {
Oliver Chen42eda3a2020-04-21 14:48:09 +0800263 tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x54012 + i) * 4);
264 ddr4_cdd[i * 2] = tmp & 0xff;
265 ddr4_cdd[i * 2 + 1] = (tmp >> 8) & 0xff;
266 }
267
268 g_cdd_rr_max[fsp] = look_for_max(ddr4_cdd, 1, 12);
269 g_cdd_ww_max[fsp] = look_for_max(ddr4_cdd, 13, 24);
270 g_cdd_rw_max[fsp] = look_for_max(ddr4_cdd, 25, 40);
271 g_cdd_wr_max[fsp] = look_for_max(ddr4_cdd, 41, 56);
272 }
273}
274
275void update_umctl2_rank_space_setting(unsigned int pstat_num)
276{
Fabio Estevam47425ef2020-07-17 16:36:53 -0300277 unsigned int i, ddr_type;
Oliver Chen42eda3a2020-04-21 14:48:09 +0800278 unsigned int addr_slot, rdata, tmp, tmp_t;
Fabio Estevam47425ef2020-07-17 16:36:53 -0300279 unsigned int ddrc_w2r, ddrc_r2w, ddrc_wr_gap, ddrc_rd_gap;
Oliver Chen42eda3a2020-04-21 14:48:09 +0800280
281 ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
282 for (i = 0; i < pstat_num; i++) {
283 addr_slot = i ? (i + 1) * 0x1000 : 0;
284 if (ddr_type == 0x20) {
285 /* update r2w:[13:8], w2r:[5:0] */
Fabio Estevam47425ef2020-07-17 16:36:53 -0300286 rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
Oliver Chen42eda3a2020-04-21 14:48:09 +0800287 ddrc_w2r = rdata & 0x3f;
Fabio Estevam47425ef2020-07-17 16:36:53 -0300288 if (is_imx8mp())
Oliver Chen42eda3a2020-04-21 14:48:09 +0800289 tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
290 else
291 tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
292 ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
293
294 ddrc_r2w = (rdata >> 8) & 0x3f;
295 if (is_imx8mp())
296 tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
297 else
298 tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
299 ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
300
301 tmp_t = (rdata & 0xffffc0c0) | (ddrc_r2w << 8) | ddrc_w2r;
302 reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
303 } else {
304 /* update w2r:[5:0] */
Fabio Estevam47425ef2020-07-17 16:36:53 -0300305 rdata = reg32_read(DDRC_DRAMTMG9(0) + addr_slot);
Oliver Chen42eda3a2020-04-21 14:48:09 +0800306 ddrc_w2r = rdata & 0x3f;
307 if (is_imx8mp())
308 tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
309 else
310 tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
311 ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
312 tmp_t = (rdata & 0xffffffc0) | ddrc_w2r;
313 reg32_write((DDRC_DRAMTMG9(0) + addr_slot), tmp_t);
314
315 /* update r2w:[13:8] */
316 rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
317 ddrc_r2w = (rdata >> 8) & 0x3f;
Fabio Estevam47425ef2020-07-17 16:36:53 -0300318 if (is_imx8mp())
Oliver Chen42eda3a2020-04-21 14:48:09 +0800319 tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
320 else
321 tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
322 ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
323
324 tmp_t = (rdata & 0xffffc0ff) | (ddrc_r2w << 8);
325 reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
326 }
327
328 if (!is_imx8mq()) {
329 /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
330 rdata = reg32_read(DDRC_RANKCTL(0) + addr_slot);
331 ddrc_wr_gap = (rdata >> 8) & 0xf;
Fabio Estevam47425ef2020-07-17 16:36:53 -0300332 if (is_imx8mp())
Oliver Chen42eda3a2020-04-21 14:48:09 +0800333 tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1);
334 else
335 tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1) + 1;
336 ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
337
338 ddrc_rd_gap = (rdata >> 4) & 0xf;
339 if (is_imx8mp())
340 tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1);
341 else
342 tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1) + 1;
343 ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
344
345 tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
346 reg32_write((DDRC_RANKCTL(0) + addr_slot), tmp_t);
347 }
348 }
349
Fabio Estevam47425ef2020-07-17 16:36:53 -0300350 if (is_imx8mq()) {
Oliver Chen42eda3a2020-04-21 14:48:09 +0800351 /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
352 rdata = reg32_read(DDRC_RANKCTL(0));
353 ddrc_wr_gap = (rdata >> 8) & 0xf;
354 tmp = ddrc_wr_gap + (g_cdd_ww_max[0] >> 1) + 1;
355 ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
356
357 ddrc_rd_gap = (rdata >> 4) & 0xf;
358 tmp = ddrc_rd_gap + (g_cdd_rr_max[0] >> 1) + 1;
359 ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
360
361 tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
362 reg32_write(DDRC_RANKCTL(0), tmp_t);
363 }
364}