blob: 0f8baefb1f89b7063c713e6851952b8d687d8241 [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;
Peng Fan692f9432018-11-20 10:19:57 +0000120 case 3200:
121 dram_pll_init(MHZ(800));
122 dram_disable_bypass();
123 break;
124 case 3000:
125 dram_pll_init(MHZ(750));
126 dram_disable_bypass();
127 break;
128 case 2400:
129 dram_pll_init(MHZ(600));
130 dram_disable_bypass();
131 break;
132 case 1600:
133 dram_pll_init(MHZ(400));
134 dram_disable_bypass();
135 break;
Jacky Baid62ddc12019-08-08 09:59:08 +0000136 case 1066:
137 dram_pll_init(MHZ(266));
138 dram_disable_bypass();
139 break;
Peng Fan692f9432018-11-20 10:19:57 +0000140 case 667:
141 dram_pll_init(MHZ(167));
142 dram_disable_bypass();
143 break;
144 case 400:
145 dram_enable_bypass(MHZ(400));
146 break;
147 case 100:
148 dram_enable_bypass(MHZ(100));
149 break;
150 default:
151 return;
152 }
153}
154
155void ddrphy_init_read_msg_block(enum fw_type type)
156{
157}
158
159void lpddr4_mr_write(unsigned int mr_rank, unsigned int mr_addr,
160 unsigned int mr_data)
161{
162 unsigned int tmp;
163 /*
164 * 1. Poll MRSTAT.mr_wr_busy until it is 0.
165 * This checks that there is no outstanding MR transaction.
166 * No writes should be performed to MRCTRL0 and MRCTRL1 if
167 * MRSTAT.mr_wr_busy = 1.
168 */
169 do {
170 tmp = reg32_read(DDRC_MRSTAT(0));
171 } while (tmp & 0x1);
172 /*
173 * 2. Write the MRCTRL0.mr_type, MRCTRL0.mr_addr, MRCTRL0.mr_rank and
174 * (for MRWs) MRCTRL1.mr_data to define the MR transaction.
175 */
176 reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4));
177 reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8) | mr_data);
178 reg32setbit(DDRC_MRCTRL0(0), 31);
179}
180
181unsigned int lpddr4_mr_read(unsigned int mr_rank, unsigned int mr_addr)
182{
183 unsigned int tmp;
184
185 reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x1);
186 do {
187 tmp = reg32_read(DDRC_MRSTAT(0));
188 } while (tmp & 0x1);
189
190 reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4) | 0x1);
191 reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8));
192 reg32setbit(DDRC_MRCTRL0(0), 31);
193 do {
194 tmp = reg32_read(DRC_PERF_MON_MRR0_DAT(0));
195 } while ((tmp & 0x8) == 0);
196 tmp = reg32_read(DRC_PERF_MON_MRR1_DAT(0));
197 tmp = tmp & 0xff;
198 reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x4);
199
200 return tmp;
201}
Oliver Chen42eda3a2020-04-21 14:48:09 +0800202
203unsigned int look_for_max(unsigned int data[],
Fabio Estevam47425ef2020-07-17 16:36:53 -0300204 unsigned int addr_start, unsigned int addr_end)
Oliver Chen42eda3a2020-04-21 14:48:09 +0800205{
206 unsigned int i, imax = 0;
207
208 for (i = addr_start; i <= addr_end; i++) {
209 if (((data[i] >> 7) == 0) && (data[i] > imax))
210 imax = data[i];
211 }
212
213 return imax;
214}
215
216void get_trained_CDD(u32 fsp)
217{
218 unsigned int i, ddr_type, tmp;
219 unsigned int cdd_cha[12], cdd_chb[12];
220 unsigned int cdd_cha_rr_max, cdd_cha_rw_max, cdd_cha_wr_max, cdd_cha_ww_max;
221 unsigned int cdd_chb_rr_max, cdd_chb_rw_max, cdd_chb_wr_max, cdd_chb_ww_max;
222
223 ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
224 if (ddr_type == 0x20) {
225 for (i = 0; i < 6; i++) {
226 tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x54013 + i) * 4);
227 cdd_cha[i * 2] = tmp & 0xff;
228 cdd_cha[i * 2 + 1] = (tmp >> 8) & 0xff;
229 }
230
231 for (i = 0; i < 7; i++) {
232 tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x5402c + i) * 4);
233 if (i == 0) {
234 cdd_cha[0] = (tmp >> 8) & 0xff;
235 } else if (i == 6) {
Fabio Estevam47425ef2020-07-17 16:36:53 -0300236 cdd_cha[11] = tmp & 0xff;
Oliver Chen42eda3a2020-04-21 14:48:09 +0800237 } else {
Fabio Estevam47425ef2020-07-17 16:36:53 -0300238 cdd_chb[i * 2 - 1] = tmp & 0xff;
Oliver Chen42eda3a2020-04-21 14:48:09 +0800239 cdd_chb[i * 2] = (tmp >> 8) & 0xff;
240 }
241 }
242
243 cdd_cha_rr_max = look_for_max(cdd_cha, 0, 1);
244 cdd_cha_rw_max = look_for_max(cdd_cha, 2, 5);
245 cdd_cha_wr_max = look_for_max(cdd_cha, 6, 9);
246 cdd_cha_ww_max = look_for_max(cdd_cha, 10, 11);
247 cdd_chb_rr_max = look_for_max(cdd_chb, 0, 1);
248 cdd_chb_rw_max = look_for_max(cdd_chb, 2, 5);
249 cdd_chb_wr_max = look_for_max(cdd_chb, 6, 9);
250 cdd_chb_ww_max = look_for_max(cdd_chb, 10, 11);
251 g_cdd_rr_max[fsp] = cdd_cha_rr_max > cdd_chb_rr_max ? cdd_cha_rr_max : cdd_chb_rr_max;
252 g_cdd_rw_max[fsp] = cdd_cha_rw_max > cdd_chb_rw_max ? cdd_cha_rw_max : cdd_chb_rw_max;
253 g_cdd_wr_max[fsp] = cdd_cha_wr_max > cdd_chb_wr_max ? cdd_cha_wr_max : cdd_chb_wr_max;
254 g_cdd_ww_max[fsp] = cdd_cha_ww_max > cdd_chb_ww_max ? cdd_cha_ww_max : cdd_chb_ww_max;
255 } else {
256 unsigned int ddr4_cdd[64];
Fabio Estevam47425ef2020-07-17 16:36:53 -0300257
258 for (i = 0; i < 29; i++) {
Oliver Chen42eda3a2020-04-21 14:48:09 +0800259 tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x54012 + i) * 4);
260 ddr4_cdd[i * 2] = tmp & 0xff;
261 ddr4_cdd[i * 2 + 1] = (tmp >> 8) & 0xff;
262 }
263
264 g_cdd_rr_max[fsp] = look_for_max(ddr4_cdd, 1, 12);
265 g_cdd_ww_max[fsp] = look_for_max(ddr4_cdd, 13, 24);
266 g_cdd_rw_max[fsp] = look_for_max(ddr4_cdd, 25, 40);
267 g_cdd_wr_max[fsp] = look_for_max(ddr4_cdd, 41, 56);
268 }
269}
270
271void update_umctl2_rank_space_setting(unsigned int pstat_num)
272{
Fabio Estevam47425ef2020-07-17 16:36:53 -0300273 unsigned int i, ddr_type;
Oliver Chen42eda3a2020-04-21 14:48:09 +0800274 unsigned int addr_slot, rdata, tmp, tmp_t;
Fabio Estevam47425ef2020-07-17 16:36:53 -0300275 unsigned int ddrc_w2r, ddrc_r2w, ddrc_wr_gap, ddrc_rd_gap;
Oliver Chen42eda3a2020-04-21 14:48:09 +0800276
277 ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
278 for (i = 0; i < pstat_num; i++) {
279 addr_slot = i ? (i + 1) * 0x1000 : 0;
280 if (ddr_type == 0x20) {
281 /* update r2w:[13:8], w2r:[5:0] */
Fabio Estevam47425ef2020-07-17 16:36:53 -0300282 rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
Oliver Chen42eda3a2020-04-21 14:48:09 +0800283 ddrc_w2r = rdata & 0x3f;
Fabio Estevam47425ef2020-07-17 16:36:53 -0300284 if (is_imx8mp())
Oliver Chen42eda3a2020-04-21 14:48:09 +0800285 tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
286 else
287 tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
288 ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
289
290 ddrc_r2w = (rdata >> 8) & 0x3f;
291 if (is_imx8mp())
292 tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
293 else
294 tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
295 ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
296
297 tmp_t = (rdata & 0xffffc0c0) | (ddrc_r2w << 8) | ddrc_w2r;
298 reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
299 } else {
300 /* update w2r:[5:0] */
Fabio Estevam47425ef2020-07-17 16:36:53 -0300301 rdata = reg32_read(DDRC_DRAMTMG9(0) + addr_slot);
Oliver Chen42eda3a2020-04-21 14:48:09 +0800302 ddrc_w2r = rdata & 0x3f;
303 if (is_imx8mp())
304 tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
305 else
306 tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
307 ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
308 tmp_t = (rdata & 0xffffffc0) | ddrc_w2r;
309 reg32_write((DDRC_DRAMTMG9(0) + addr_slot), tmp_t);
310
311 /* update r2w:[13:8] */
312 rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
313 ddrc_r2w = (rdata >> 8) & 0x3f;
Fabio Estevam47425ef2020-07-17 16:36:53 -0300314 if (is_imx8mp())
Oliver Chen42eda3a2020-04-21 14:48:09 +0800315 tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
316 else
317 tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
318 ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
319
320 tmp_t = (rdata & 0xffffc0ff) | (ddrc_r2w << 8);
321 reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
322 }
323
324 if (!is_imx8mq()) {
325 /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
326 rdata = reg32_read(DDRC_RANKCTL(0) + addr_slot);
327 ddrc_wr_gap = (rdata >> 8) & 0xf;
Fabio Estevam47425ef2020-07-17 16:36:53 -0300328 if (is_imx8mp())
Oliver Chen42eda3a2020-04-21 14:48:09 +0800329 tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1);
330 else
331 tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1) + 1;
332 ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
333
334 ddrc_rd_gap = (rdata >> 4) & 0xf;
335 if (is_imx8mp())
336 tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1);
337 else
338 tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1) + 1;
339 ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
340
341 tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
342 reg32_write((DDRC_RANKCTL(0) + addr_slot), tmp_t);
343 }
344 }
345
Fabio Estevam47425ef2020-07-17 16:36:53 -0300346 if (is_imx8mq()) {
Oliver Chen42eda3a2020-04-21 14:48:09 +0800347 /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
348 rdata = reg32_read(DDRC_RANKCTL(0));
349 ddrc_wr_gap = (rdata >> 8) & 0xf;
350 tmp = ddrc_wr_gap + (g_cdd_ww_max[0] >> 1) + 1;
351 ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
352
353 ddrc_rd_gap = (rdata >> 4) & 0xf;
354 tmp = ddrc_rd_gap + (g_cdd_rr_max[0] >> 1) + 1;
355 ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
356
357 tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
358 reg32_write(DDRC_RANKCTL(0), tmp_t);
359 }
360}