Merge tag 'u-boot-imx-20200825' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
[platform/kernel/u-boot.git] / drivers / ram / sifive / fu540_ddr.c
1 // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
2 /*
3  * (C) Copyright 2020 SiFive, Inc.
4  *
5  * Authors:
6  *   Pragnesh Patel <pragnesh.patel@sifive.com>
7  */
8
9 #include <common.h>
10 #include <dm.h>
11 #include <fdtdec.h>
12 #include <init.h>
13 #include <ram.h>
14 #include <regmap.h>
15 #include <syscon.h>
16 #include <asm/io.h>
17 #include <clk.h>
18 #include <wait_bit.h>
19 #include <linux/bitops.h>
20
21 #define DENALI_CTL_0    0
22 #define DENALI_CTL_21   21
23 #define DENALI_CTL_120  120
24 #define DENALI_CTL_132  132
25 #define DENALI_CTL_136  136
26 #define DENALI_CTL_170  170
27 #define DENALI_CTL_181  181
28 #define DENALI_CTL_182  182
29 #define DENALI_CTL_184  184
30 #define DENALI_CTL_208  208
31 #define DENALI_CTL_209  209
32 #define DENALI_CTL_210  210
33 #define DENALI_CTL_212  212
34 #define DENALI_CTL_214  214
35 #define DENALI_CTL_216  216
36 #define DENALI_CTL_224  224
37 #define DENALI_CTL_225  225
38 #define DENALI_CTL_260  260
39
40 #define DENALI_PHY_1152 1152
41 #define DENALI_PHY_1214 1214
42
43 #define DRAM_CLASS_OFFSET                       8
44 #define DRAM_CLASS_DDR4                         0xA
45 #define OPTIMAL_RMODW_EN_OFFSET                 0
46 #define DISABLE_RD_INTERLEAVE_OFFSET            16
47 #define OUT_OF_RANGE_OFFSET                     1
48 #define MULTIPLE_OUT_OF_RANGE_OFFSET            2
49 #define PORT_COMMAND_CHANNEL_ERROR_OFFSET       7
50 #define MC_INIT_COMPLETE_OFFSET                 8
51 #define LEVELING_OPERATION_COMPLETED_OFFSET     22
52 #define DFI_PHY_WRLELV_MODE_OFFSET              24
53 #define DFI_PHY_RDLVL_MODE_OFFSET               24
54 #define DFI_PHY_RDLVL_GATE_MODE_OFFSET          0
55 #define VREF_EN_OFFSET                          24
56 #define PORT_ADDR_PROTECTION_EN_OFFSET          0
57 #define AXI0_ADDRESS_RANGE_ENABLE               8
58 #define AXI0_RANGE_PROT_BITS_0_OFFSET           24
59 #define RDLVL_EN_OFFSET                         16
60 #define RDLVL_GATE_EN_OFFSET                    24
61 #define WRLVL_EN_OFFSET                         0
62
63 #define PHY_RX_CAL_DQ0_0_OFFSET                 0
64 #define PHY_RX_CAL_DQ1_0_OFFSET                 16
65
66 DECLARE_GLOBAL_DATA_PTR;
67
68 struct fu540_ddrctl {
69         volatile u32 denali_ctl[265];
70 };
71
72 struct fu540_ddrphy {
73         volatile u32 denali_phy[1215];
74 };
75
76 /**
77  * struct fu540_ddr_info
78  *
79  * @dev                         : pointer for the device
80  * @info                        : UCLASS RAM information
81  * @ctl                         : DDR controller base address
82  * @phy                         : DDR PHY base address
83  * @ctrl                        : DDR control base address
84  * @physical_filter_ctrl        : DDR physical filter control base address
85  */
86 struct fu540_ddr_info {
87         struct udevice *dev;
88         struct ram_info info;
89         struct fu540_ddrctl *ctl;
90         struct fu540_ddrphy *phy;
91         struct clk ddr_clk;
92         u32 *physical_filter_ctrl;
93 };
94
95 #if defined(CONFIG_SPL_BUILD)
96 struct fu540_ddr_params {
97         struct fu540_ddrctl pctl_regs;
98         struct fu540_ddrphy phy_regs;
99 };
100
101 struct sifive_dmc_plat {
102         struct fu540_ddr_params ddr_params;
103 };
104
105 /*
106  * TODO : It can be possible to use common sdram_copy_to_reg() API
107  * n: Unit bytes
108  */
109 static void sdram_copy_to_reg(volatile u32 *dest,
110                               volatile u32 *src, u32 n)
111 {
112         int i;
113
114         for (i = 0; i < n / sizeof(u32); i++) {
115                 writel(*src, dest);
116                 src++;
117                 dest++;
118         }
119 }
120
121 static void fu540_ddr_setup_range_protection(volatile u32 *ctl, u64 end_addr)
122 {
123         u32 end_addr_16kblocks = ((end_addr >> 14) & 0x7FFFFF) - 1;
124
125         writel(0x0, DENALI_CTL_209 + ctl);
126         writel(end_addr_16kblocks, DENALI_CTL_210 + ctl);
127         writel(0x0, DENALI_CTL_212 + ctl);
128         writel(0x0, DENALI_CTL_214 + ctl);
129         writel(0x0, DENALI_CTL_216 + ctl);
130         setbits_le32(DENALI_CTL_224 + ctl,
131                      0x3 << AXI0_RANGE_PROT_BITS_0_OFFSET);
132         writel(0xFFFFFFFF, DENALI_CTL_225 + ctl);
133         setbits_le32(DENALI_CTL_208 + ctl, 0x1 << AXI0_ADDRESS_RANGE_ENABLE);
134         setbits_le32(DENALI_CTL_208 + ctl,
135                      0x1 << PORT_ADDR_PROTECTION_EN_OFFSET);
136 }
137
138 static void fu540_ddr_start(volatile u32 *ctl, u32 *physical_filter_ctrl,
139                             u64 ddr_end)
140 {
141         volatile u64 *filterreg = (volatile u64 *)physical_filter_ctrl;
142
143         setbits_le32(DENALI_CTL_0 + ctl, 0x1);
144
145         wait_for_bit_le32((void *)ctl + DENALI_CTL_132,
146                           BIT(MC_INIT_COMPLETE_OFFSET), false, 100, false);
147
148         /* Disable the BusBlocker in front of the controller AXI slave ports */
149         filterreg[0] = 0x0f00000000000000UL | (ddr_end >> 2);
150 }
151
152 static void fu540_ddr_check_errata(u32 regbase, u32 updownreg)
153 {
154         u64 fails     = 0;
155         u32 dq        = 0;
156         u32 down, up;
157         u8 failc0, failc1;
158         u32 phy_rx_cal_dqn_0_offset;
159
160         for (u32 bit = 0; bit < 2; bit++) {
161                 if (bit == 0) {
162                         phy_rx_cal_dqn_0_offset =
163                                 PHY_RX_CAL_DQ0_0_OFFSET;
164                 } else {
165                         phy_rx_cal_dqn_0_offset =
166                                 PHY_RX_CAL_DQ1_0_OFFSET;
167                 }
168
169                 down = (updownreg >>
170                         phy_rx_cal_dqn_0_offset) & 0x3F;
171                 up = (updownreg >>
172                       (phy_rx_cal_dqn_0_offset + 6)) &
173                       0x3F;
174
175                 failc0 = ((down == 0) && (up == 0x3F));
176                 failc1 = ((up == 0) && (down == 0x3F));
177
178                 /* print error message on failure */
179                 if (failc0 || failc1) {
180                         if (fails == 0)
181                                 printf("DDR error in fixing up\n");
182
183                         fails |= (1 << dq);
184
185                         char slicelsc = '0';
186                         char slicemsc = '0';
187
188                         slicelsc += (dq % 10);
189                         slicemsc += (dq / 10);
190                         printf("S ");
191                         printf("%c", slicemsc);
192                         printf("%c", slicelsc);
193
194                         if (failc0)
195                                 printf("U");
196                         else
197                                 printf("D");
198
199                         printf("\n");
200                 }
201                 dq++;
202         }
203 }
204
205 static u64 fu540_ddr_phy_fixup(volatile u32 *ddrphyreg)
206 {
207         u32 slicebase = 0;
208
209         /* check errata condition */
210         for (u32 slice = 0; slice < 8; slice++) {
211                 u32 regbase = slicebase + 34;
212
213                 for (u32 reg = 0; reg < 4; reg++) {
214                         u32 updownreg = readl(regbase + reg + ddrphyreg);
215
216                         fu540_ddr_check_errata(regbase, updownreg);
217                 }
218                 slicebase += 128;
219         }
220
221         return(0);
222 }
223
224 static u32 fu540_ddr_get_dram_class(volatile u32 *ctl)
225 {
226         u32 reg = readl(DENALI_CTL_0 + ctl);
227
228         return ((reg >> DRAM_CLASS_OFFSET) & 0xF);
229 }
230
231 static int fu540_ddr_setup(struct udevice *dev)
232 {
233         struct fu540_ddr_info *priv = dev_get_priv(dev);
234         struct sifive_dmc_plat *plat = dev_get_platdata(dev);
235         struct fu540_ddr_params *params = &plat->ddr_params;
236         volatile u32 *denali_ctl =  priv->ctl->denali_ctl;
237         volatile u32 *denali_phy =  priv->phy->denali_phy;
238         const u64 ddr_size = priv->info.size;
239         const u64 ddr_end = priv->info.base + ddr_size;
240         int ret, i;
241         u32 physet;
242
243         ret = dev_read_u32_array(dev, "sifive,ddr-params",
244                                  (u32 *)&plat->ddr_params,
245                                  sizeof(plat->ddr_params) / sizeof(u32));
246         if (ret) {
247                 printf("%s: Cannot read sifive,ddr-params %d\n",
248                        __func__, ret);
249                 return ret;
250         }
251
252         sdram_copy_to_reg(priv->ctl->denali_ctl,
253                           params->pctl_regs.denali_ctl,
254                           sizeof(struct fu540_ddrctl));
255
256         /* phy reset */
257         for (i = DENALI_PHY_1152; i <= DENALI_PHY_1214; i++) {
258                 physet = params->phy_regs.denali_phy[i];
259                 priv->phy->denali_phy[i] = physet;
260         }
261
262         for (i = 0; i < DENALI_PHY_1152; i++) {
263                 physet = params->phy_regs.denali_phy[i];
264                 priv->phy->denali_phy[i] = physet;
265         }
266
267         /* Disable read interleave DENALI_CTL_120 */
268         setbits_le32(DENALI_CTL_120 + denali_ctl,
269                      1 << DISABLE_RD_INTERLEAVE_OFFSET);
270
271         /* Disable optimal read/modify/write logic DENALI_CTL_21 */
272         clrbits_le32(DENALI_CTL_21 + denali_ctl, 1 << OPTIMAL_RMODW_EN_OFFSET);
273
274         /* Enable write Leveling DENALI_CTL_170 */
275         setbits_le32(DENALI_CTL_170 + denali_ctl, (1 << WRLVL_EN_OFFSET)
276                      | (1 << DFI_PHY_WRLELV_MODE_OFFSET));
277
278         /* Enable read leveling DENALI_CTL_181 and DENALI_CTL_260 */
279         setbits_le32(DENALI_CTL_181 + denali_ctl,
280                      1 << DFI_PHY_RDLVL_MODE_OFFSET);
281         setbits_le32(DENALI_CTL_260 + denali_ctl, 1 << RDLVL_EN_OFFSET);
282
283         /* Enable read leveling gate DENALI_CTL_260 and DENALI_CTL_182 */
284         setbits_le32(DENALI_CTL_260 + denali_ctl, 1 << RDLVL_GATE_EN_OFFSET);
285         setbits_le32(DENALI_CTL_182 + denali_ctl,
286                      1 << DFI_PHY_RDLVL_GATE_MODE_OFFSET);
287
288         if (fu540_ddr_get_dram_class(denali_ctl) == DRAM_CLASS_DDR4) {
289                 /* Enable vref training DENALI_CTL_184 */
290                 setbits_le32(DENALI_CTL_184 + denali_ctl, 1 << VREF_EN_OFFSET);
291         }
292
293         /* Mask off leveling completion interrupt DENALI_CTL_136 */
294         setbits_le32(DENALI_CTL_136 + denali_ctl,
295                      1 << LEVELING_OPERATION_COMPLETED_OFFSET);
296
297         /* Mask off MC init complete interrupt DENALI_CTL_136 */
298         setbits_le32(DENALI_CTL_136 + denali_ctl, 1 << MC_INIT_COMPLETE_OFFSET);
299
300         /* Mask off out of range interrupts DENALI_CTL_136 */
301         setbits_le32(DENALI_CTL_136 + denali_ctl, (1 << OUT_OF_RANGE_OFFSET)
302                      | (1 << MULTIPLE_OUT_OF_RANGE_OFFSET));
303
304         /* set up range protection */
305         fu540_ddr_setup_range_protection(denali_ctl, priv->info.size);
306
307         /* Mask off port command error interrupt DENALI_CTL_136 */
308         setbits_le32(DENALI_CTL_136 + denali_ctl,
309                      1 << PORT_COMMAND_CHANNEL_ERROR_OFFSET);
310
311         fu540_ddr_start(denali_ctl, priv->physical_filter_ctrl, ddr_end);
312
313         fu540_ddr_phy_fixup(denali_phy);
314
315         /* check size */
316         priv->info.size = get_ram_size((long *)priv->info.base,
317                                        ddr_size);
318
319         debug("%s : %lx\n", __func__, (uintptr_t)priv->info.size);
320
321         /* check memory access for all memory */
322         if (priv->info.size != ddr_size) {
323                 printf("DDR invalid size : 0x%lx, expected 0x%lx\n",
324                        (uintptr_t)priv->info.size, (uintptr_t)ddr_size);
325                 return -EINVAL;
326         }
327
328         return 0;
329 }
330 #endif
331
332 static int fu540_ddr_probe(struct udevice *dev)
333 {
334         struct fu540_ddr_info *priv = dev_get_priv(dev);
335
336         /* Read memory base and size from DT */
337         fdtdec_setup_mem_size_base();
338         priv->info.base = gd->ram_base;
339         priv->info.size = gd->ram_size;
340
341 #if defined(CONFIG_SPL_BUILD)
342         struct regmap *map;
343         int ret;
344         u32 clock = 0;
345
346         debug("FU540 DDR probe\n");
347         priv->dev = dev;
348
349         ret = regmap_init_mem(dev_ofnode(dev), &map);
350         if (ret)
351                 return ret;
352
353         ret = clk_get_by_index(dev, 0, &priv->ddr_clk);
354         if (ret) {
355                 debug("clk get failed %d\n", ret);
356                 return ret;
357         }
358
359         ret = dev_read_u32(dev, "clock-frequency", &clock);
360         if (ret) {
361                 debug("clock-frequency not found in dt %d\n", ret);
362                 return ret;
363         } else {
364                 ret = clk_set_rate(&priv->ddr_clk, clock);
365                 if (ret < 0) {
366                         debug("Could not set DDR clock\n");
367                         return ret;
368                 }
369         }
370
371         ret = clk_enable(&priv->ddr_clk);
372         priv->ctl = regmap_get_range(map, 0);
373         priv->phy = regmap_get_range(map, 1);
374         priv->physical_filter_ctrl = regmap_get_range(map, 2);
375
376         return fu540_ddr_setup(dev);
377 #endif
378
379         return 0;
380 }
381
382 static int fu540_ddr_get_info(struct udevice *dev, struct ram_info *info)
383 {
384         struct fu540_ddr_info *priv = dev_get_priv(dev);
385
386         *info = priv->info;
387
388         return 0;
389 }
390
391 static struct ram_ops fu540_ddr_ops = {
392         .get_info = fu540_ddr_get_info,
393 };
394
395 static const struct udevice_id fu540_ddr_ids[] = {
396         { .compatible = "sifive,fu540-c000-ddr" },
397         { }
398 };
399
400 U_BOOT_DRIVER(fu540_ddr) = {
401         .name = "fu540_ddr",
402         .id = UCLASS_RAM,
403         .of_match = fu540_ddr_ids,
404         .ops = &fu540_ddr_ops,
405         .probe = fu540_ddr_probe,
406         .priv_auto_alloc_size = sizeof(struct fu540_ddr_info),
407 #if defined(CONFIG_SPL_BUILD)
408         .platdata_auto_alloc_size = sizeof(struct sifive_dmc_plat),
409 #endif
410 };