Merge branch 'master' of git://git.denx.de/u-boot-spi
[platform/kernel/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/kernel.h>
11 #include <linux/printk.h>
12 #include <linux/sizes.h>
13 #include <asm/global_data.h>
14
15 #include "sg-regs.h"
16 #include "soc-info.h"
17
18 DECLARE_GLOBAL_DATA_PTR;
19
20 struct uniphier_memif_data {
21         unsigned int soc_id;
22         unsigned long sparse_ch1_base;
23         int have_ch2;
24 };
25
26 static const struct uniphier_memif_data uniphier_memif_data[] = {
27         {
28                 .soc_id = UNIPHIER_LD4_ID,
29                 .sparse_ch1_base = 0xc0000000,
30         },
31         {
32                 .soc_id = UNIPHIER_PRO4_ID,
33                 .sparse_ch1_base = 0xa0000000,
34         },
35         {
36                 .soc_id = UNIPHIER_SLD8_ID,
37                 .sparse_ch1_base = 0xc0000000,
38         },
39         {
40                 .soc_id = UNIPHIER_PRO5_ID,
41                 .sparse_ch1_base = 0xc0000000,
42         },
43         {
44                 .soc_id = UNIPHIER_PXS2_ID,
45                 .sparse_ch1_base = 0xc0000000,
46                 .have_ch2 = 1,
47         },
48         {
49                 .soc_id = UNIPHIER_LD6B_ID,
50                 .sparse_ch1_base = 0xc0000000,
51                 .have_ch2 = 1,
52         },
53         {
54                 .soc_id = UNIPHIER_LD11_ID,
55                 .sparse_ch1_base = 0xc0000000,
56         },
57         {
58                 .soc_id = UNIPHIER_LD20_ID,
59                 .sparse_ch1_base = 0xc0000000,
60                 .have_ch2 = 1,
61         },
62         {
63                 .soc_id = UNIPHIER_PXS3_ID,
64                 .sparse_ch1_base = 0xc0000000,
65                 .have_ch2 = 1,
66         },
67 };
68 UNIPHIER_DEFINE_SOCDATA_FUNC(uniphier_get_memif_data, uniphier_memif_data)
69
70 struct uniphier_dram_map {
71         unsigned long base;
72         unsigned long size;
73 };
74
75 static int uniphier_memconf_decode(struct uniphier_dram_map *dram_map)
76 {
77         const struct uniphier_memif_data *data;
78         unsigned long size;
79         u32 val;
80
81         data = uniphier_get_memif_data();
82         if (!data) {
83                 pr_err("unsupported SoC\n");
84                 return -EINVAL;
85         }
86
87         val = readl(SG_MEMCONF);
88
89         /* set up ch0 */
90         dram_map[0].base = CONFIG_SYS_SDRAM_BASE;
91
92         switch (val & SG_MEMCONF_CH0_SZ_MASK) {
93         case SG_MEMCONF_CH0_SZ_64M:
94                 size = SZ_64M;
95                 break;
96         case SG_MEMCONF_CH0_SZ_128M:
97                 size = SZ_128M;
98                 break;
99         case SG_MEMCONF_CH0_SZ_256M:
100                 size = SZ_256M;
101                 break;
102         case SG_MEMCONF_CH0_SZ_512M:
103                 size = SZ_512M;
104                 break;
105         case SG_MEMCONF_CH0_SZ_1G:
106                 size = SZ_1G;
107                 break;
108         default:
109                 pr_err("error: invalid value is set to MEMCONF ch0 size\n");
110                 return -EINVAL;
111         }
112
113         if ((val & SG_MEMCONF_CH0_NUM_MASK) == SG_MEMCONF_CH0_NUM_2)
114                 size *= 2;
115
116         dram_map[0].size = size;
117
118         /* set up ch1 */
119         dram_map[1].base = dram_map[0].base + size;
120
121         if (val & SG_MEMCONF_SPARSEMEM) {
122                 if (dram_map[1].base > data->sparse_ch1_base) {
123                         pr_warn("Sparse mem is enabled, but ch0 and ch1 overlap\n");
124                         pr_warn("Only ch0 is available\n");
125                         dram_map[1].base = 0;
126                         return 0;
127                 }
128
129                 dram_map[1].base = data->sparse_ch1_base;
130         }
131
132         switch (val & SG_MEMCONF_CH1_SZ_MASK) {
133         case SG_MEMCONF_CH1_SZ_64M:
134                 size = SZ_64M;
135                 break;
136         case SG_MEMCONF_CH1_SZ_128M:
137                 size = SZ_128M;
138                 break;
139         case SG_MEMCONF_CH1_SZ_256M:
140                 size = SZ_256M;
141                 break;
142         case SG_MEMCONF_CH1_SZ_512M:
143                 size = SZ_512M;
144                 break;
145         case SG_MEMCONF_CH1_SZ_1G:
146                 size = SZ_1G;
147                 break;
148         default:
149                 pr_err("error: invalid value is set to MEMCONF ch1 size\n");
150                 return -EINVAL;
151         }
152
153         if ((val & SG_MEMCONF_CH1_NUM_MASK) == SG_MEMCONF_CH1_NUM_2)
154                 size *= 2;
155
156         dram_map[1].size = size;
157
158         if (!data->have_ch2 || val & SG_MEMCONF_CH2_DISABLE)
159                 return 0;
160
161         /* set up ch2 */
162         dram_map[2].base = dram_map[1].base + size;
163
164         switch (val & SG_MEMCONF_CH2_SZ_MASK) {
165         case SG_MEMCONF_CH2_SZ_64M:
166                 size = SZ_64M;
167                 break;
168         case SG_MEMCONF_CH2_SZ_128M:
169                 size = SZ_128M;
170                 break;
171         case SG_MEMCONF_CH2_SZ_256M:
172                 size = SZ_256M;
173                 break;
174         case SG_MEMCONF_CH2_SZ_512M:
175                 size = SZ_512M;
176                 break;
177         case SG_MEMCONF_CH2_SZ_1G:
178                 size = SZ_1G;
179                 break;
180         default:
181                 pr_err("error: invalid value is set to MEMCONF ch2 size\n");
182                 return -EINVAL;
183         }
184
185         if ((val & SG_MEMCONF_CH2_NUM_MASK) == SG_MEMCONF_CH2_NUM_2)
186                 size *= 2;
187
188         dram_map[2].size = size;
189
190         return 0;
191 }
192
193 int dram_init(void)
194 {
195         struct uniphier_dram_map dram_map[3] = {};
196         int ret, i;
197
198         gd->ram_size = 0;
199
200         ret = uniphier_memconf_decode(dram_map);
201         if (ret)
202                 return ret;
203
204         for (i = 0; i < ARRAY_SIZE(dram_map); i++) {
205                 unsigned long max_size;
206
207                 if (!dram_map[i].size)
208                         break;
209
210                 /*
211                  * U-Boot relocates itself to the tail of the memory region,
212                  * but it does not expect sparse memory.  We use the first
213                  * contiguous chunk here.
214                  */
215                 if (i > 0 && dram_map[i - 1].base + dram_map[i - 1].size <
216                                                         dram_map[i].base)
217                         break;
218
219                 /*
220                  * Do not use memory that exceeds 32bit address range.  U-Boot
221                  * relocates itself to the end of the effectively available RAM.
222                  * This could be a problem for DMA engines that do not support
223                  * 64bit address (SDMA of SDHCI, UniPhier AV-ether, etc.)
224                  */
225                 if (dram_map[i].base >= 1ULL << 32)
226                         break;
227
228                 max_size = (1ULL << 32) - dram_map[i].base;
229
230                 if (dram_map[i].size > max_size) {
231                         gd->ram_size += max_size;
232                         break;
233                 }
234
235                 gd->ram_size += dram_map[i].size;
236         }
237
238         /*
239          * LD20 uses the last 64 byte for each channel for dynamic
240          * DDR PHY training
241          */
242         if (uniphier_get_soc_id() == UNIPHIER_LD20_ID)
243                 gd->ram_size -= 64;
244
245         return 0;
246 }
247
248 int dram_init_banksize(void)
249 {
250         struct uniphier_dram_map dram_map[3] = {};
251         int i;
252
253         uniphier_memconf_decode(dram_map);
254
255         for (i = 0; i < ARRAY_SIZE(dram_map); i++) {
256                 if (i >= ARRAY_SIZE(gd->bd->bi_dram))
257                         break;
258
259                 gd->bd->bi_dram[i].start = dram_map[i].base;
260                 gd->bd->bi_dram[i].size = dram_map[i].size;
261         }
262
263         return 0;
264 }