blob: 42dd0dbf18faee6ae3c59923e72a6c4ade540181 [file] [log] [blame]
Ying-Chun Liu (PaulLiu)a97107f2021-04-22 04:50:31 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2017 NXP
4 * Copyright 2020 Linaro
5 *
6 */
7
8#include <common.h>
9#include <spl.h>
10#include <asm/io.h>
11#include <errno.h>
12#include <command.h>
13#include <asm/io.h>
14#include <asm/arch/lpddr4_define.h>
15#include <asm/mach-imx/iomux-v3.h>
16#include <asm/mach-imx/gpio.h>
17#include <asm-generic/gpio.h>
18#include <asm/arch/ddr.h>
19#include <asm/arch/imx8mq_pins.h>
20#include <asm/arch/sys_proto.h>
21#include <asm/arch/clock.h>
22#include <asm/mach-imx/gpio.h>
23#include "ddr.h"
24
25static unsigned int lpddr4_mr_read(unsigned int mr_rank, unsigned int mr_addr)
26{
27 unsigned int tmp;
28
29 reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x1);
30 do {
31 tmp = reg32_read(DDRC_MRSTAT(0));
32 } while (tmp & 0x1);
33
34 reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4) | 0x1);
35 reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8));
36 reg32setbit(DDRC_MRCTRL0(0), 31);
37 do {
38 tmp = reg32_read(DRC_PERF_MON_MRR0_DAT(0));
39 } while ((tmp & 0x8) == 0);
40 tmp = reg32_read(DRC_PERF_MON_MRR1_DAT(0));
41 reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x4);
42 while (tmp) { //try to find a significant byte in the word
43 if (tmp & 0xff) {
44 tmp &= 0xff;
45 break;
46 }
47 tmp >>= 8;
48 }
49 return tmp;
50}
51
52struct lpddr4_desc {
53 char name[16];
54 unsigned int id;
55 unsigned int size;
56 unsigned int count;
57 /* an optional field
58 * use it if default is not the
59 * 1-st array entry
60 */
61 unsigned int _default;
62 /* An optional field to distiguish DRAM chips that
63 * have different geometry, though return the same MRR.
64 * Default value 0xff
65 */
66 u8 subind;
67 struct dram_timing_info *timing;
68 char *desc[4];
69};
70
71#define DEFAULT (('D' << 24) + ('E' << 16) + ('F' << 8) + 'A')
72static const struct lpddr4_desc lpddr4_array[] = {
73 { .name = "Nanya", .id = 0x05000010, .subind = 0xff,
74 .size = 2048, .count = 1, .timing = &ucm_dram_timing_01061010},
75 { .name = "Samsung", .id = 0x01061010, .subind = 0xff,
76 .size = 2048, .count = 1, .timing = &ucm_dram_timing_01061010},
77 { .name = "Kingston", .id = 0xff000010, .subind = 0x04,
78 .size = 4096, .count = 1, .timing = &ucm_dram_timing_ff000110},
79 { .name = "Kingston", .id = 0xff000010, .subind = 0x02,
80 .size = 2048, .count = 1, .timing = &ucm_dram_timing_01061010},
81 { .name = "Micron", .id = 0xff020008, .subind = 0xff,
82 .size = 2048, .count = 1, .timing = &ucm_dram_timing_ff020008},
83 { .name = "Micron", .id = 0xff000110, .subind = 0xff,
84 .size = 4096, .count = 1, .timing = &ucm_dram_timing_ff000110},
85};
86
87static unsigned int lpddr4_get_mr(void)
88{
89 int i = 0, attempts = 5;
90 unsigned int ddr_info = 0;
91 unsigned int regs[] = { 5, 6, 7, 8 };
92
93 do {
94 for (i = 0 ; i < ARRAY_SIZE(regs) ; i++) {
95 unsigned int data = 0;
96
97 data = lpddr4_mr_read(0xF, regs[i]);
98 ddr_info <<= 8;
99 ddr_info += (data & 0xFF);
100 }
101 if (ddr_info != 0xFFFFFFFF && ddr_info != 0)
102 break; // The attempt was successful
103 } while (--attempts);
104 return ddr_info;
105}
106
107static void spl_tcm_init(struct lpddr4_tcm_desc *lpddr4_tcm_desc)
108{
109 if (lpddr4_tcm_desc->sign == DEFAULT)
110 return;
111
112 lpddr4_tcm_desc->sign = DEFAULT;
113 lpddr4_tcm_desc->index = 0;
114}
115
116static void spl_tcm_fini(struct lpddr4_tcm_desc *lpddr4_tcm_desc)
117{
118 if (lpddr4_tcm_desc->sign != DEFAULT)
119 return;
120
121 lpddr4_tcm_desc->sign = ~DEFAULT;
122 lpddr4_tcm_desc->index = 0;
123}
124
125#define SPL_TCM_DATA 0x7e0000
126#define SPL_TCM_INIT spl_tcm_init(lpddr4_tcm_desc)
127#define SPL_TCM_FINI spl_tcm_fini(lpddr4_tcm_desc)
128
129void spl_dram_init_compulab(void)
130{
131 unsigned int ddr_info = 0xdeadbeef;
132 unsigned int ddr_info_mrr = 0xdeadbeef;
133 unsigned int ddr_found = 0;
134 int i = 0;
135
136 struct lpddr4_tcm_desc *lpddr4_tcm_desc =
137 (struct lpddr4_tcm_desc *)SPL_TCM_DATA;
138
139 if (lpddr4_tcm_desc->sign != DEFAULT) {
140 /* if not in tcm scan mode */
141 for (i = 0; i < ARRAY_SIZE(lpddr4_array); i++) {
142 if (lpddr4_array[i].id == ddr_info &&
143 lpddr4_array[i].subind == 0xff) {
144 ddr_found = 1;
145 break;
146 }
147 }
148 }
149
150 /* Walk trought all available ddr ids and apply
151 * one by one. Save the index at the tcm memory that
152 * persists after the reset.
153 */
154 if (ddr_found == 0) {
155 SPL_TCM_INIT;
156
157 if (lpddr4_tcm_desc->index < ARRAY_SIZE(lpddr4_array)) {
158 printf("DDRINFO: Cfg attempt: [ %d/%lu ]\n",
159 lpddr4_tcm_desc->index + 1,
160 ARRAY_SIZE(lpddr4_array));
161 i = lpddr4_tcm_desc->index;
162 lpddr4_tcm_desc->index += 1;
163 } else {
164 /* Ran out all available ddr setings */
165 printf("DDRINFO: Ran out all [ %lu ] cfg attempts. A non supported configuration.\n",
166 ARRAY_SIZE(lpddr4_array));
167 while (1)
168 ;
169 }
170 ddr_info = lpddr4_array[i].id;
171 } else {
172 printf("DDRINFO(%s): %s %dG\n", (ddr_found ? "D" : "?"),
173 lpddr4_array[i].name,
174 lpddr4_array[i].size);
175 }
176
177 if (ddr_init(lpddr4_array[i].timing)) {
178 SPL_TCM_INIT;
179 do_reset(NULL, 0, 0, NULL);
180 }
181
182 ddr_info_mrr = lpddr4_get_mr();
183 if (ddr_info_mrr == 0xFFFFFFFF) {
184 printf("DDRINFO(M): mr5-8 [ 0x%x ] is invalid; reset\n",
185 ddr_info_mrr);
186 SPL_TCM_INIT;
187 do_reset(NULL, 0, 0, NULL);
188 }
189
190 printf("DDRINFO(M): mr5-8 [ 0x%x ]\n", ddr_info_mrr);
191 printf("DDRINFO(%s): mr5-8 [ 0x%x ]\n", (ddr_found ? "E" : "T"),
192 ddr_info);
193
194 if (ddr_info_mrr != ddr_info) {
195 SPL_TCM_INIT;
196 do_reset(NULL, 0, 0, NULL);
197 }
198
199 SPL_TCM_FINI;
200
201 /* Pass the dram size to th U-Boot through the tcm memory */
202 { /* To figure out what to store into the TCM buffer */
203 /* For debug purpouse only. To override the real memsize */
204 unsigned int ddr_tcm_size = 0;
205
206 if (ddr_tcm_size == 0 || ddr_tcm_size == -1)
207 ddr_tcm_size = lpddr4_array[i].size;
208
209 lpddr4_tcm_desc->size = ddr_tcm_size;
210 }
211}