1 /*
2  * Copyright (C) 2012-2015 Panasonic Corporation
3  * Copyright (C) 2015-2017 Socionext Inc.
4  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 
9 #include <common.h>
10 #include <fdtdec.h>
11 #include <linux/errno.h>
12 #include <linux/sizes.h>
13 
14 #include "sg-regs.h"
15 #include "soc-info.h"
16 
17 #define pr_warn(fmt, args...)	printf(fmt, ##args)
18 #define pr_err(fmt, args...)	printf(fmt, ##args)
19 
20 DECLARE_GLOBAL_DATA_PTR;
21 
22 struct uniphier_memif_data {
23 	unsigned int soc_id;
24 	unsigned long sparse_ch1_base;
25 	int have_ch2;
26 };
27 
28 static const struct uniphier_memif_data uniphier_memif_data[] = {
29 	{
30 		.soc_id = UNIPHIER_SLD3_ID,
31 		.sparse_ch1_base = 0xc0000000,
32 		/*
33 		 * In fact, SLD3 has DRAM ch2, but the memory regions for ch1
34 		 * and ch2 overlap, and host cannot get access to them at the
35 		 * same time.  Hide the ch2 from U-Boot.
36 		 */
37 	},
38 	{
39 		.soc_id = UNIPHIER_LD4_ID,
40 		.sparse_ch1_base = 0xc0000000,
41 	},
42 	{
43 		.soc_id = UNIPHIER_PRO4_ID,
44 		.sparse_ch1_base = 0xa0000000,
45 	},
46 	{
47 		.soc_id = UNIPHIER_SLD8_ID,
48 		.sparse_ch1_base = 0xc0000000,
49 	},
50 	{
51 		.soc_id = UNIPHIER_PRO5_ID,
52 		.sparse_ch1_base = 0xc0000000,
53 	},
54 	{
55 		.soc_id = UNIPHIER_PXS2_ID,
56 		.sparse_ch1_base = 0xc0000000,
57 		.have_ch2 = 1,
58 	},
59 	{
60 		.soc_id = UNIPHIER_LD6B_ID,
61 		.sparse_ch1_base = 0xc0000000,
62 		.have_ch2 = 1,
63 	},
64 	{
65 		.soc_id = UNIPHIER_LD11_ID,
66 		.sparse_ch1_base = 0xc0000000,
67 	},
68 	{
69 		.soc_id = UNIPHIER_LD20_ID,
70 		.sparse_ch1_base = 0xc0000000,
71 		.have_ch2 = 1,
72 	},
73 	{
74 		.soc_id = UNIPHIER_PXS3_ID,
75 		.sparse_ch1_base = 0xc0000000,
76 		.have_ch2 = 1,
77 	},
78 };
79 UNIPHIER_DEFINE_SOCDATA_FUNC(uniphier_get_memif_data, uniphier_memif_data)
80 
81 struct uniphier_dram_map {
82 	unsigned long base;
83 	unsigned long size;
84 };
85 
86 static int uniphier_memconf_decode(struct uniphier_dram_map *dram_map)
87 {
88 	const struct uniphier_memif_data *data;
89 	unsigned long size;
90 	u32 val;
91 
92 	data = uniphier_get_memif_data();
93 	if (!data) {
94 		pr_err("unsupported SoC\n");
95 		return -EINVAL;
96 	}
97 
98 	val = readl(SG_MEMCONF);
99 
100 	/* set up ch0 */
101 	dram_map[0].base = CONFIG_SYS_SDRAM_BASE;
102 
103 	switch (val & SG_MEMCONF_CH0_SZ_MASK) {
104 	case SG_MEMCONF_CH0_SZ_64M:
105 		size = SZ_64M;
106 		break;
107 	case SG_MEMCONF_CH0_SZ_128M:
108 		size = SZ_128M;
109 		break;
110 	case SG_MEMCONF_CH0_SZ_256M:
111 		size = SZ_256M;
112 		break;
113 	case SG_MEMCONF_CH0_SZ_512M:
114 		size = SZ_512M;
115 		break;
116 	case SG_MEMCONF_CH0_SZ_1G:
117 		size = SZ_1G;
118 		break;
119 	default:
120 		pr_err("error: invalid value is set to MEMCONF ch0 size\n");
121 		return -EINVAL;
122 	}
123 
124 	if ((val & SG_MEMCONF_CH0_NUM_MASK) == SG_MEMCONF_CH0_NUM_2)
125 		size *= 2;
126 
127 	dram_map[0].size = size;
128 
129 	/* set up ch1 */
130 	dram_map[1].base = dram_map[0].base + size;
131 
132 	if (val & SG_MEMCONF_SPARSEMEM) {
133 		if (dram_map[1].base > data->sparse_ch1_base) {
134 			pr_warn("Sparse mem is enabled, but ch0 and ch1 overlap\n");
135 			pr_warn("Only ch0 is available\n");
136 			dram_map[1].base = 0;
137 			return 0;
138 		}
139 
140 		dram_map[1].base = data->sparse_ch1_base;
141 	}
142 
143 	switch (val & SG_MEMCONF_CH1_SZ_MASK) {
144 	case SG_MEMCONF_CH1_SZ_64M:
145 		size = SZ_64M;
146 		break;
147 	case SG_MEMCONF_CH1_SZ_128M:
148 		size = SZ_128M;
149 		break;
150 	case SG_MEMCONF_CH1_SZ_256M:
151 		size = SZ_256M;
152 		break;
153 	case SG_MEMCONF_CH1_SZ_512M:
154 		size = SZ_512M;
155 		break;
156 	case SG_MEMCONF_CH1_SZ_1G:
157 		size = SZ_1G;
158 		break;
159 	default:
160 		pr_err("error: invalid value is set to MEMCONF ch1 size\n");
161 		return -EINVAL;
162 	}
163 
164 	if ((val & SG_MEMCONF_CH1_NUM_MASK) == SG_MEMCONF_CH1_NUM_2)
165 		size *= 2;
166 
167 	dram_map[1].size = size;
168 
169 	if (!data->have_ch2 || val & SG_MEMCONF_CH2_DISABLE)
170 		return 0;
171 
172 	/* set up ch2 */
173 	dram_map[2].base = dram_map[1].base + size;
174 
175 	switch (val & SG_MEMCONF_CH2_SZ_MASK) {
176 	case SG_MEMCONF_CH2_SZ_64M:
177 		size = SZ_64M;
178 		break;
179 	case SG_MEMCONF_CH2_SZ_128M:
180 		size = SZ_128M;
181 		break;
182 	case SG_MEMCONF_CH2_SZ_256M:
183 		size = SZ_256M;
184 		break;
185 	case SG_MEMCONF_CH2_SZ_512M:
186 		size = SZ_512M;
187 		break;
188 	case SG_MEMCONF_CH2_SZ_1G:
189 		size = SZ_1G;
190 		break;
191 	default:
192 		pr_err("error: invalid value is set to MEMCONF ch2 size\n");
193 		return -EINVAL;
194 	}
195 
196 	if ((val & SG_MEMCONF_CH2_NUM_MASK) == SG_MEMCONF_CH2_NUM_2)
197 		size *= 2;
198 
199 	dram_map[2].size = size;
200 
201 	return 0;
202 }
203 
204 int dram_init(void)
205 {
206 	struct uniphier_dram_map dram_map[3] = {};
207 	int ret, i;
208 
209 	gd->ram_size = 0;
210 
211 	ret = uniphier_memconf_decode(dram_map);
212 	if (ret)
213 		return ret;
214 
215 	for (i = 0; i < ARRAY_SIZE(dram_map); i++) {
216 
217 		if (!dram_map[i].size)
218 			break;
219 
220 		/*
221 		 * U-Boot relocates itself to the tail of the memory region,
222 		 * but it does not expect sparse memory.  We use the first
223 		 * contiguous chunk here.
224 		 */
225 		if (i > 0 && dram_map[i - 1].base + dram_map[i - 1].size <
226 							dram_map[i].base)
227 			break;
228 
229 		gd->ram_size += dram_map[i].size;
230 	}
231 
232 	return 0;
233 }
234 
235 int dram_init_banksize(void)
236 {
237 	struct uniphier_dram_map dram_map[3] = {};
238 	int i;
239 
240 	uniphier_memconf_decode(dram_map);
241 
242 	for (i = 0; i < ARRAY_SIZE(dram_map); i++) {
243 		if (i >= ARRAY_SIZE(gd->bd->bi_dram))
244 			break;
245 
246 		gd->bd->bi_dram[i].start = dram_map[i].base;
247 		gd->bd->bi_dram[i].size = dram_map[i].size;
248 	}
249 
250 	return 0;
251 }
252 
253 #ifdef CONFIG_OF_BOARD_SETUP
254 /*
255  * The DRAM PHY requires 64 byte scratch area in each DRAM channel
256  * for its dynamic PHY training feature.
257  */
258 int ft_board_setup(void *fdt, bd_t *bd)
259 {
260 	unsigned long rsv_addr;
261 	const unsigned long rsv_size = 64;
262 	int i, ret;
263 
264 	if (uniphier_get_soc_id() != UNIPHIER_LD20_ID)
265 		return 0;
266 
267 	for (i = 0; i < ARRAY_SIZE(gd->bd->bi_dram); i++) {
268 		if (!gd->bd->bi_dram[i].size)
269 			continue;
270 
271 		rsv_addr = gd->bd->bi_dram[i].start + gd->bd->bi_dram[i].size;
272 		rsv_addr -= rsv_size;
273 
274 		ret = fdt_add_mem_rsv(fdt, rsv_addr, rsv_size);
275 		if (ret)
276 			return -ENOSPC;
277 
278 		printf("   Reserved memory region for DRAM PHY training: addr=%lx size=%lx\n",
279 		       rsv_addr, rsv_size);
280 	}
281 
282 	return 0;
283 }
284 #endif
285