blob: 762619a960f38b3b7d12e372a7e41633148e6f60 [file] [log] [blame]
developer29b37c52020-04-21 09:28:34 +02001// 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>
Simon Glass3ba929a2020-10-30 21:38:53 -060011#include <asm/global_data.h>
developer29b37c52020-04-21 09:28:34 +020012#include <linux/bitops.h>
13#include <linux/io.h>
14#include <mach/mc.h>
15
16DECLARE_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
31static inline void pref_op(int op, const volatile void *addr)
32{
33 __asm__ __volatile__("pref %0, 0(%1)" : : "i" (op), "r" (addr));
34}
35
Stefan Roese6877cdb2020-03-06 15:14:03 +010036static inline bool dqs_test_error(void __iomem *memc, u32 memsize, u32 dqsval,
37 u32 bias)
developer29b37c52020-04-21 09:28:34 +020038{
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)
Stefan Roese6877cdb2020-03-06 15:14:03 +010068 return true;
developer29b37c52020-04-21 09:28:34 +020069 }
70 }
71
Stefan Roese6877cdb2020-03-06 15:14:03 +010072 return false;
developer29b37c52020-04-21 09:28:34 +020073}
74
Stefan Roese1a33eab2020-03-06 15:14:04 +010075static inline int dqs_find_max(void __iomem *memc, u32 memsize, int initval,
76 int maxval, int shift, u32 regval)
developer29b37c52020-04-21 09:28:34 +020077{
Stefan Roese142b0322020-03-06 15:14:05 +010078 int fieldval;
Stefan Roese1a33eab2020-03-06 15:14:04 +010079 u32 dqsval;
developer29b37c52020-04-21 09:28:34 +020080
Stefan Roese142b0322020-03-06 15:14:05 +010081 for (fieldval = initval; fieldval <= maxval; fieldval++) {
developer29b37c52020-04-21 09:28:34 +020082 dqsval = regval | (fieldval << shift);
Stefan Roese6877cdb2020-03-06 15:14:03 +010083 if (dqs_test_error(memc, memsize, dqsval, 3))
Stefan Roese142b0322020-03-06 15:14:05 +010084 return max(fieldval - 1, initval);
85 }
developer29b37c52020-04-21 09:28:34 +020086
Stefan Roese142b0322020-03-06 15:14:05 +010087 return maxval;
developer29b37c52020-04-21 09:28:34 +020088}
89
Stefan Roese1a33eab2020-03-06 15:14:04 +010090static inline int dqs_find_min(void __iomem *memc, u32 memsize, int initval,
91 int minval, int shift, u32 regval)
developer29b37c52020-04-21 09:28:34 +020092{
Stefan Roese142b0322020-03-06 15:14:05 +010093 int fieldval;
Stefan Roese1a33eab2020-03-06 15:14:04 +010094 u32 dqsval;
developer29b37c52020-04-21 09:28:34 +020095
Stefan Roese142b0322020-03-06 15:14:05 +010096 for (fieldval = initval; fieldval >= minval; fieldval--) {
developer29b37c52020-04-21 09:28:34 +020097 dqsval = regval | (fieldval << shift);
Stefan Roese142b0322020-03-06 15:14:05 +010098 if (dqs_test_error(memc, memsize, dqsval, 1))
99 return min(fieldval + 1, initval);
developer29b37c52020-04-21 09:28:34 +0200100 }
101
Stefan Roese142b0322020-03-06 15:14:05 +0100102 return minval;
developer29b37c52020-04-21 09:28:34 +0200103}
104
105void 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}