0ea7c7d5e1cc612464f45517693158fbb34dec5e
[oweals/u-boot.git] / arch / mips / mach-mtmips / ddr_cal.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020 MediaTek Inc.
4  *
5  * Author:  Weijie Gao <weijie.gao@mediatek.com>
6  */
7
8 #include <common.h>
9 #include <asm/addrspace.h>
10 #include <asm/cacheops.h>
11 #include <linux/bitops.h>
12 #include <linux/io.h>
13 #include <mach/mc.h>
14
15 DECLARE_GLOBAL_DATA_PTR;
16
17 #define COARSE_MIN_START        6
18 #define FINE_MIN_START          15
19 #define COARSE_MAX_START        7
20 #define FINE_MAX_START          0
21
22 #define NUM_OF_CACHELINE        128
23 #define TEST_PAT_SIZE           (NUM_OF_CACHELINE * CONFIG_SYS_CACHELINE_SIZE)
24
25 #define INIT_DQS_VAL            ((7 << DQS1_DELAY_COARSE_TUNING_S) | \
26                                 (4 << DQS1_DELAY_FINE_TUNING_S) | \
27                                 (7 << DQS0_DELAY_COARSE_TUNING_S) | \
28                                 (4 << DQS0_DELAY_FINE_TUNING_S))
29
30 static inline void pref_op(int op, const volatile void *addr)
31 {
32         __asm__ __volatile__("pref %0, 0(%1)" : : "i" (op), "r" (addr));
33 }
34
35 static inline int dqs_test_valid(void __iomem *memc, u32 memsize, u32 dqsval,
36                                  u32 bias)
37 {
38         u32 *nca, *ca;
39         u32 off;
40         int i;
41
42         for (off = 0; off < memsize - TEST_PAT_SIZE; off += (memsize >> 6)) {
43                 nca = (u32 *)KSEG1ADDR(off);
44                 ca = (u32 *)KSEG0ADDR(off);
45
46                 writel(INIT_DQS_VAL, memc + MEMCTL_DDR_DQS_DLY_REG);
47                 wmb();
48
49                 for (i = 0; i < TEST_PAT_SIZE / sizeof(u32); i++)
50                         ca[i] = 0x1f1f1f1f;
51
52                 for (i = 0; i < TEST_PAT_SIZE / sizeof(u32); i++)
53                         nca[i] = (u32)nca + i + bias;
54
55                 writel(dqsval, memc + MEMCTL_DDR_DQS_DLY_REG);
56                 wmb();
57
58                 for (i = 0; i < TEST_PAT_SIZE; i += CONFIG_SYS_CACHELINE_SIZE)
59                         mips_cache(HIT_INVALIDATE_D, (u8 *)ca + i);
60                 wmb();
61
62                 for (i = 0; i < TEST_PAT_SIZE; i += CONFIG_SYS_CACHELINE_SIZE)
63                         pref_op(0, (u8 *)ca + i);
64
65                 for (i = 0; i < TEST_PAT_SIZE / sizeof(u32); i++) {
66                         if (ca[i] != (u32)nca + i + bias)
67                                 return -1;
68                 }
69         }
70
71         return 0;
72 }
73
74 static inline u32 dqs_find_max(void __iomem *memc, u32 memsize, u32 initval,
75                                u32 maxval, u32 shift, u32 regval)
76 {
77         u32 fieldval = initval, dqsval;
78
79         do {
80                 dqsval = regval | (fieldval << shift);
81
82                 if (dqs_test_valid(memc, memsize, dqsval, 3))
83                         break;
84
85                 fieldval++;
86         } while (fieldval <= maxval);
87
88         return fieldval;
89 }
90
91 static inline u32 dqs_find_min(void __iomem *memc, u32 memsize, u32 initval,
92                                u32 minval, u32 shift, u32 regval)
93 {
94         u32 fieldval = initval, dqsval;
95
96         while (fieldval > minval) {
97                 dqsval = regval | (fieldval << shift);
98
99                 if (dqs_test_valid(memc, memsize, dqsval, 1)) {
100                         fieldval++;
101                         break;
102                 }
103
104                 fieldval--;
105         }
106
107         return fieldval;
108 }
109
110 void ddr_calibrate(void __iomem *memc, u32 memsize, u32 bw)
111 {
112         u32 dqs_coarse_min, dqs_coarse_max, dqs_coarse_val;
113         u32 dqs_fine_min, dqs_fine_max, dqs_fine_val;
114         u32 dqs_coarse_min_limit, dqs_fine_min_limit;
115         u32 dlls, dqs_dll, ddr_cfg2_reg;
116         u32 dqs_dly_tmp, dqs_dly, test_dqs, shift;
117         u32 rem, mask;
118         int i;
119
120         /* Disable Self-refresh */
121         clrbits_32(memc + MEMCTL_DDR_SELF_REFRESH_REG, SR_AUTO_EN);
122
123         /* Save DDR_CFG2 and modify its DQS gating window */
124         ddr_cfg2_reg = readl(memc + MEMCTL_DDR_CFG2_REG);
125         mask = DQS0_GATING_WINDOW_M;
126         if (bw == IND_SDRAM_WIDTH_16BIT)
127                 mask |= DQS1_GATING_WINDOW_M;
128         clrbits_32(memc + MEMCTL_DDR_CFG2_REG, mask);
129
130         /* Get minimum available DQS value */
131         dlls = readl(memc + MEMCTL_DLL_DBG_REG);
132         dlls = (dlls & MST_DLY_SEL_M) >> MST_DLY_SEL_S;
133
134         dqs_dll = dlls >> 4;
135         if (dqs_dll <= 8)
136                 dqs_coarse_min_limit = 8 - dqs_dll;
137         else
138                 dqs_coarse_min_limit = 0;
139
140         dqs_dll = dlls & 0xf;
141         if (dqs_dll <= 8)
142                 dqs_fine_min_limit = 8 - dqs_dll;
143         else
144                 dqs_fine_min_limit = 0;
145
146         /* Initial DQS register value */
147         dqs_dly = INIT_DQS_VAL;
148
149         /* Calibrate DQS0 and/or DQS1 */
150         for (i = 0; i < bw; i++) {
151                 shift = i * 8;
152                 dqs_dly &= ~(0xff << shift);
153
154                 /* Find maximum DQS coarse-grain */
155                 dqs_dly_tmp = dqs_dly | (0xf << shift);
156                 dqs_coarse_max = dqs_find_max(memc, memsize, COARSE_MAX_START,
157                                               0xf, 4 + shift, dqs_dly_tmp);
158
159                 /* Find maximum DQS fine-grain */
160                 dqs_dly_tmp = dqs_dly | (dqs_coarse_max << (4 + shift));
161                 test_dqs = dqs_find_max(memc, memsize, FINE_MAX_START, 0xf,
162                                         shift, dqs_dly_tmp);
163
164                 if (test_dqs == FINE_MAX_START) {
165                         dqs_coarse_max--;
166                         dqs_fine_max = 0xf;
167                 } else {
168                         dqs_fine_max = test_dqs - 1;
169                 }
170
171                 /* Find minimum DQS coarse-grain */
172                 dqs_dly_tmp = dqs_dly;
173                 dqs_coarse_min = dqs_find_min(memc, memsize, COARSE_MIN_START,
174                                               dqs_coarse_min_limit, 4 + shift,
175                                               dqs_dly_tmp);
176
177                 /* Find minimum DQS fine-grain */
178                 dqs_dly_tmp = dqs_dly | (dqs_coarse_min << (4 + shift));
179                 test_dqs = dqs_find_min(memc, memsize, FINE_MIN_START,
180                                         dqs_fine_min_limit, shift, dqs_dly_tmp);
181
182                 if (test_dqs == FINE_MIN_START + 1) {
183                         dqs_coarse_min++;
184                         dqs_fine_min = 0;
185                 } else {
186                         dqs_fine_min = test_dqs;
187                 }
188
189                 /* Calculate central DQS coarse/fine value */
190                 dqs_coarse_val = (dqs_coarse_max + dqs_coarse_min) >> 1;
191                 rem = (dqs_coarse_max + dqs_coarse_min) % 2;
192
193                 dqs_fine_val = (rem * 4) + ((dqs_fine_max + dqs_fine_min) >> 1);
194                 if (dqs_fine_val >= 0x10) {
195                         dqs_coarse_val++;
196                         dqs_fine_val -= 8;
197                 }
198
199                 /* Save current DQS value */
200                 dqs_dly |= ((dqs_coarse_val << 4) | dqs_fine_val) << shift;
201         }
202
203         /* Set final DQS value */
204         writel(dqs_dly, memc + MEMCTL_DDR_DQS_DLY_REG);
205
206         /* Restore DDR_CFG2 */
207         writel(ddr_cfg2_reg, memc + MEMCTL_DDR_CFG2_REG);
208
209         /* Enable Self-refresh */
210         setbits_32(memc + MEMCTL_DDR_SELF_REFRESH_REG, SR_AUTO_EN);
211 }