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