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