ARM: uniphier: make mem_map run-time configurable
[oweals/u-boot.git] / arch / arm / mach-uniphier / dram_init.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2012-2015 Panasonic Corporation
4  * Copyright (C) 2015-2017 Socionext Inc.
5  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
6  */
7
8 #include <common.h>
9 #include <linux/errno.h>
10 #include <linux/io.h>
11 #include <linux/kernel.h>
12 #include <linux/printk.h>
13 #include <linux/sizes.h>
14 #include <asm/global_data.h>
15
16 #include "init.h"
17 #include "sg-regs.h"
18 #include "soc-info.h"
19
20 DECLARE_GLOBAL_DATA_PTR;
21
22 struct uniphier_dram_map {
23         unsigned long base;
24         unsigned long size;
25 };
26
27 static int uniphier_memconf_decode(struct uniphier_dram_map *dram_map,
28                                    unsigned long sparse_ch1_base, bool have_ch2)
29 {
30         unsigned long size;
31         u32 val;
32
33         val = readl(sg_base + SG_MEMCONF);
34
35         /* set up ch0 */
36         dram_map[0].base = CONFIG_SYS_SDRAM_BASE;
37
38         switch (val & SG_MEMCONF_CH0_SZ_MASK) {
39         case SG_MEMCONF_CH0_SZ_64M:
40                 size = SZ_64M;
41                 break;
42         case SG_MEMCONF_CH0_SZ_128M:
43                 size = SZ_128M;
44                 break;
45         case SG_MEMCONF_CH0_SZ_256M:
46                 size = SZ_256M;
47                 break;
48         case SG_MEMCONF_CH0_SZ_512M:
49                 size = SZ_512M;
50                 break;
51         case SG_MEMCONF_CH0_SZ_1G:
52                 size = SZ_1G;
53                 break;
54         default:
55                 pr_err("error: invalid value is set to MEMCONF ch0 size\n");
56                 return -EINVAL;
57         }
58
59         if ((val & SG_MEMCONF_CH0_NUM_MASK) == SG_MEMCONF_CH0_NUM_2)
60                 size *= 2;
61
62         dram_map[0].size = size;
63
64         /* set up ch1 */
65         dram_map[1].base = dram_map[0].base + size;
66
67         if (val & SG_MEMCONF_SPARSEMEM) {
68                 if (dram_map[1].base > sparse_ch1_base) {
69                         pr_warn("Sparse mem is enabled, but ch0 and ch1 overlap\n");
70                         pr_warn("Only ch0 is available\n");
71                         dram_map[1].base = 0;
72                         return 0;
73                 }
74
75                 dram_map[1].base = sparse_ch1_base;
76         }
77
78         switch (val & SG_MEMCONF_CH1_SZ_MASK) {
79         case SG_MEMCONF_CH1_SZ_64M:
80                 size = SZ_64M;
81                 break;
82         case SG_MEMCONF_CH1_SZ_128M:
83                 size = SZ_128M;
84                 break;
85         case SG_MEMCONF_CH1_SZ_256M:
86                 size = SZ_256M;
87                 break;
88         case SG_MEMCONF_CH1_SZ_512M:
89                 size = SZ_512M;
90                 break;
91         case SG_MEMCONF_CH1_SZ_1G:
92                 size = SZ_1G;
93                 break;
94         default:
95                 pr_err("error: invalid value is set to MEMCONF ch1 size\n");
96                 return -EINVAL;
97         }
98
99         if ((val & SG_MEMCONF_CH1_NUM_MASK) == SG_MEMCONF_CH1_NUM_2)
100                 size *= 2;
101
102         dram_map[1].size = size;
103
104         if (!have_ch2 || val & SG_MEMCONF_CH2_DISABLE)
105                 return 0;
106
107         /* set up ch2 */
108         dram_map[2].base = dram_map[1].base + size;
109
110         switch (val & SG_MEMCONF_CH2_SZ_MASK) {
111         case SG_MEMCONF_CH2_SZ_64M:
112                 size = SZ_64M;
113                 break;
114         case SG_MEMCONF_CH2_SZ_128M:
115                 size = SZ_128M;
116                 break;
117         case SG_MEMCONF_CH2_SZ_256M:
118                 size = SZ_256M;
119                 break;
120         case SG_MEMCONF_CH2_SZ_512M:
121                 size = SZ_512M;
122                 break;
123         case SG_MEMCONF_CH2_SZ_1G:
124                 size = SZ_1G;
125                 break;
126         default:
127                 pr_err("error: invalid value is set to MEMCONF ch2 size\n");
128                 return -EINVAL;
129         }
130
131         if ((val & SG_MEMCONF_CH2_NUM_MASK) == SG_MEMCONF_CH2_NUM_2)
132                 size *= 2;
133
134         dram_map[2].size = size;
135
136         return 0;
137 }
138
139 static int uniphier_ld4_dram_map_get(struct uniphier_dram_map dram_map[])
140 {
141         return uniphier_memconf_decode(dram_map, 0xc0000000, false);
142 }
143
144 static int uniphier_pro4_dram_map_get(struct uniphier_dram_map dram_map[])
145 {
146         return uniphier_memconf_decode(dram_map, 0xa0000000, false);
147 }
148
149 static int uniphier_pxs2_dram_map_get(struct uniphier_dram_map dram_map[])
150 {
151         return uniphier_memconf_decode(dram_map, 0xc0000000, true);
152 }
153
154 struct uniphier_dram_init_data {
155         unsigned int soc_id;
156         int (*dram_map_get)(struct uniphier_dram_map dram_map[]);
157 };
158
159 static const struct uniphier_dram_init_data uniphier_dram_init_data[] = {
160         {
161                 .soc_id = UNIPHIER_LD4_ID,
162                 .dram_map_get = uniphier_ld4_dram_map_get,
163         },
164         {
165                 .soc_id = UNIPHIER_PRO4_ID,
166                 .dram_map_get = uniphier_pro4_dram_map_get,
167         },
168         {
169                 .soc_id = UNIPHIER_SLD8_ID,
170                 .dram_map_get = uniphier_ld4_dram_map_get,
171         },
172         {
173                 .soc_id = UNIPHIER_PRO5_ID,
174                 .dram_map_get = uniphier_ld4_dram_map_get,
175         },
176         {
177                 .soc_id = UNIPHIER_PXS2_ID,
178                 .dram_map_get = uniphier_pxs2_dram_map_get,
179         },
180         {
181                 .soc_id = UNIPHIER_LD6B_ID,
182                 .dram_map_get = uniphier_pxs2_dram_map_get,
183         },
184         {
185                 .soc_id = UNIPHIER_LD11_ID,
186                 .dram_map_get = uniphier_ld4_dram_map_get,
187         },
188         {
189                 .soc_id = UNIPHIER_LD20_ID,
190                 .dram_map_get = uniphier_pxs2_dram_map_get,
191         },
192         {
193                 .soc_id = UNIPHIER_PXS3_ID,
194                 .dram_map_get = uniphier_pxs2_dram_map_get,
195         },
196 };
197 UNIPHIER_DEFINE_SOCDATA_FUNC(uniphier_get_dram_init_data,
198                              uniphier_dram_init_data)
199
200 static int uniphier_dram_map_get(struct uniphier_dram_map *dram_map)
201 {
202         const struct uniphier_dram_init_data *data;
203
204         data = uniphier_get_dram_init_data();
205         if (!data) {
206                 pr_err("unsupported SoC\n");
207                 return -ENOTSUPP;
208         }
209
210         return data->dram_map_get(dram_map);
211 }
212
213 int dram_init(void)
214 {
215         struct uniphier_dram_map dram_map[3] = {};
216         bool valid_bank_found = false;
217         unsigned long prev_top;
218         int ret, i;
219
220         gd->ram_size = 0;
221
222         ret = uniphier_dram_map_get(dram_map);
223         if (ret)
224                 return ret;
225
226         for (i = 0; i < ARRAY_SIZE(dram_map); i++) {
227                 unsigned long max_size;
228
229                 if (!dram_map[i].size)
230                         continue;
231
232                 /*
233                  * U-Boot relocates itself to the tail of the memory region,
234                  * but it does not expect sparse memory.  We use the first
235                  * contiguous chunk here.
236                  */
237                 if (valid_bank_found && prev_top < dram_map[i].base)
238                         break;
239
240                 /*
241                  * Do not use memory that exceeds 32bit address range.  U-Boot
242                  * relocates itself to the end of the effectively available RAM.
243                  * This could be a problem for DMA engines that do not support
244                  * 64bit address (SDMA of SDHCI, UniPhier AV-ether, etc.)
245                  */
246                 if (dram_map[i].base >= 1ULL << 32)
247                         break;
248
249                 max_size = (1ULL << 32) - dram_map[i].base;
250
251                 if (dram_map[i].size > max_size) {
252                         gd->ram_size += max_size;
253                         break;
254                 }
255
256                 gd->ram_size += dram_map[i].size;
257
258                 prev_top = dram_map[i].base + dram_map[i].size;
259                 valid_bank_found = true;
260         }
261
262         /*
263          * LD20 uses the last 64 byte for each channel for dynamic
264          * DDR PHY training
265          */
266         if (uniphier_get_soc_id() == UNIPHIER_LD20_ID)
267                 gd->ram_size -= 64;
268
269         return 0;
270 }
271
272 int dram_init_banksize(void)
273 {
274         struct uniphier_dram_map dram_map[3] = {};
275         unsigned long base, top;
276         bool valid_bank_found = false;
277         int ret, i;
278
279         ret = uniphier_dram_map_get(dram_map);
280         if (ret)
281                 return ret;
282
283         for (i = 0; i < ARRAY_SIZE(dram_map); i++) {
284                 if (i < ARRAY_SIZE(gd->bd->bi_dram)) {
285                         gd->bd->bi_dram[i].start = dram_map[i].base;
286                         gd->bd->bi_dram[i].size = dram_map[i].size;
287                 }
288
289                 if (!dram_map[i].size)
290                         continue;
291
292                 if (!valid_bank_found)
293                         base = dram_map[i].base;
294                 top = dram_map[i].base + dram_map[i].size;
295                 valid_bank_found = true;
296         }
297
298         if (!valid_bank_found)
299                 return -EINVAL;
300
301         /* map all the DRAM regions */
302         uniphier_mem_map_init(base, top - base);
303
304         return 0;
305 }