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