blob: c6c092ba2a948d43641d50808af3dcfd9ae092d1 [file] [log] [blame]
Angel Pons4b429832020-04-02 23:48:50 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
Patrick Georgi2efc8802012-11-06 11:03:53 +01003
4#include <stdint.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +02005#include <device/mmio.h>
Patrick Georgi2efc8802012-11-06 11:03:53 +01006#include <console/console.h>
7#include "gm45.h"
8
9#define CxRECy_MCHBAR(x, y) (0x14a0 + (x * 0x0100) + ((3 - y) * 4))
10#define CxRECy_SHIFT_L 0
11#define CxRECy_MASK_L (3 << CxRECy_SHIFT_L)
12#define CxRECy_SHIFT_H 16
13#define CxRECy_MASK_H (3 << CxRECy_SHIFT_H)
14#define CxRECy_T_SHIFT 28
15#define CxRECy_T_MASK (0xf << CxRECy_T_SHIFT)
16#define CxRECy_T(t) ((t << CxRECy_T_SHIFT) & CxRECy_T_MASK)
17#define CxRECy_P_SHIFT 24
18#define CxRECy_P_MASK (0x7 << CxRECy_P_SHIFT)
19#define CxRECy_P(p) ((p << CxRECy_P_SHIFT) & CxRECy_P_MASK)
20#define CxRECy_PH_SHIFT 22
21#define CxRECy_PH_MASK (0x3 << CxRECy_PH_SHIFT)
22#define CxRECy_PH(p) ((p << CxRECy_PH_SHIFT) & CxRECy_PH_MASK)
23#define CxRECy_PM_SHIFT 20
24#define CxRECy_PM_MASK (0x3 << CxRECy_PM_SHIFT)
25#define CxRECy_PM(p) ((p << CxRECy_PM_SHIFT) & CxRECy_PM_MASK)
26#define CxRECy_TIMING_MASK (CxRECy_T_MASK | CxRECy_P_MASK | \
27 CxRECy_PH_MASK | CxRECy_PM_MASK)
28
29#define CxDRT3_C_SHIFT 7
30#define CxDRT3_C_MASK (0xf << CxDRT3_C_SHIFT)
31#define CxDRT3_C(c) ((c << CxDRT3_C_SHIFT) & CxDRT3_C_MASK)
32/* group to byte-lane mapping: (cardF X group X 2 per group) */
33static const char bytelane_map[2][4][2] = {
34/* A,B,C */{ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } },
35/* F */{ { 0, 2 }, { 1, 3 }, { 4, 6 }, { 5, 7 } },
36};
37
38#define PH_BOUND 4
39#define PH_STEP 2
40#define PM_BOUND 3
41#define C_BOUND 16
42typedef struct {
43 int c;
44 int pre;
45 int ph;
46 int t;
47 const int t_bound;
48 int p;
49 const int p_bound;
50} rec_timing_t;
51static void normalize_rec_timing(rec_timing_t *const timing)
52{
53 while (timing->p >= timing->p_bound) {
54 timing->t++;
55 timing->p -= timing->p_bound;
56 }
57 while (timing->p < 0) {
58 timing->t--;
59 timing->p += timing->p_bound;
60 }
61 while (timing->t >= timing->t_bound) {
62 timing->ph += PH_STEP;
63 timing->t -= timing->t_bound;
64 }
65 while (timing->t < 0) {
66 timing->ph -= PH_STEP;
67 timing->t += timing->t_bound;
68 }
69 while (timing->ph >= PH_BOUND) {
70 timing->c++;
71 timing->ph -= PH_BOUND;
72 }
73 while (timing->ph < 0) {
74 timing->c--;
75 timing->ph += PH_BOUND;
76 }
77 if (timing->c < 0 || timing->c >= C_BOUND)
78 die("Timing under-/overflow during "
79 "receive-enable calibration.\n");
80}
81
82static void rec_full_backstep(rec_timing_t *const timing)
83{
84 timing->c--;
85}
86static void rec_half_backstep(rec_timing_t *const timing)
87{
88 timing->ph -= PH_STEP;
89}
90static void rec_quarter_step(rec_timing_t *const timing)
91{
92 timing->t += (timing->t_bound) >> 1;
93 timing->p += (timing->t_bound & 1) * (timing->p_bound >> 1);
94}
95static void rec_quarter_backstep(rec_timing_t *const timing)
96{
97 timing->t -= (timing->t_bound) >> 1;
98 timing->p -= (timing->t_bound & 1) * (timing->p_bound >> 1);
99}
100static void rec_smallest_step(rec_timing_t *const timing)
101{
102 timing->p++;
103}
104
105static void program_timing(int channel, int group,
106 rec_timing_t timings[][4])
107{
108 rec_timing_t *const timing = &timings[channel][group];
109
110 normalize_rec_timing(timing);
111
112 /* C value is per channel. */
113 unsigned int mchbar = CxDRT3_MCHBAR(channel);
114 MCHBAR32(mchbar) = (MCHBAR32(mchbar) & ~CxDRT3_C_MASK) |
115 CxDRT3_C(timing->c);
116
117 /* All other per group. */
118 mchbar = CxRECy_MCHBAR(channel, group);
119 u32 reg = MCHBAR32(mchbar);
120 reg &= ~CxRECy_TIMING_MASK;
121 reg |= CxRECy_T(timing->t) | CxRECy_P(timing->p) |
122 CxRECy_PH(timing->ph) | CxRECy_PM(timing->pre);
123 MCHBAR32(mchbar) = reg;
124}
125
126static int read_dqs_level(const int channel, const int lane)
127{
128 unsigned int mchbar = 0x14f0 + (channel * 0x0100);
129 MCHBAR32(mchbar) &= ~(1 << 9);
130 MCHBAR32(mchbar) |= (1 << 9);
131
132 /* Read from this channel. */
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -0800133 read32((u32 *)raminit_get_rank_addr(channel, 0));
Patrick Georgi2efc8802012-11-06 11:03:53 +0100134
135 mchbar = 0x14b0 + (channel * 0x0100) + ((7 - lane) * 4);
136 return MCHBAR32(mchbar) & (1 << 30);
137}
138
139static void find_dqs_low(const int channel, const int group,
140 rec_timing_t timings[][4], const char lane_map[][2])
141{
142 /* Look for DQS low, using quarter steps. */
143 while (read_dqs_level(channel, lane_map[group][0]) ||
144 read_dqs_level(channel, lane_map[group][1])) {
145 rec_quarter_step(&timings[channel][group]);
146 program_timing(channel, group, timings);
147 }
148}
149static void find_dqs_high(const int channel, const int group,
150 rec_timing_t timings[][4], const char lane_map[][2])
151{
152 /* Look for _any_ DQS high, using quarter steps. */
153 while (!read_dqs_level(channel, lane_map[group][0]) &&
154 !read_dqs_level(channel, lane_map[group][1])) {
155 rec_quarter_step(&timings[channel][group]);
156 program_timing(channel, group, timings);
157 }
158}
159static void find_dqs_edge_lowhigh(const int channel, const int group,
160 rec_timing_t timings[][4],
161 const char lane_map[][2])
162{
163 /* Advance beyond previous high to low transition. */
164 timings[channel][group].t += 2;
165 program_timing(channel, group, timings);
166
167 /* Coarsely look for DQS high. */
168 find_dqs_high(channel, group, timings, lane_map);
169
170 /* Go back and perform finer search. */
171 rec_quarter_backstep(&timings[channel][group]);
172 program_timing(channel, group, timings);
173 while (!read_dqs_level(channel, lane_map[group][0]) ||
174 !read_dqs_level(channel, lane_map[group][1])) {
175 rec_smallest_step(&timings[channel][group]);
176 program_timing(channel, group, timings);
177 }
178}
179static void find_preamble(const int channel, const int group,
180 rec_timing_t timings[][4], const char lane_map[][2])
181{
182 /* Look for DQS low, backstepping. */
183 while (read_dqs_level(channel, lane_map[group][0]) ||
184 read_dqs_level(channel, lane_map[group][1])) {
185 rec_full_backstep(&timings[channel][group]);
186 program_timing(channel, group, timings);
187 }
188}
189
190static void receive_enable_calibration(const timings_t *const timings,
191 const dimminfo_t *const dimms)
192{
193 /* Override group to byte-lane mapping for raw card type F DIMMS. */
194 static const char over_bytelane_map[2][4][2] = {
195 /* A,B,C */{ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } },
196 /* F */{ { 0, 0 }, { 3, 3 }, { 6, 6 }, { 5, 5 } },
197 };
198
Tristan Corrick267d0862017-05-10 22:31:14 +1200199 const int cardF[] = {
200 dimms[0].card_type == 0xf,
Tristan Corrick12e65622017-05-10 22:40:41 +1200201 dimms[1].card_type == 0xf,
Tristan Corrick267d0862017-05-10 22:31:14 +1200202 };
203
204 const unsigned int t_bound =
Patrick Georgi2efc8802012-11-06 11:03:53 +0100205 (timings->mem_clock == MEM_CLOCK_1067MT) ? 9 : 12;
Tristan Corrick267d0862017-05-10 22:31:14 +1200206 const unsigned int p_bound =
Patrick Georgi2efc8802012-11-06 11:03:53 +0100207 (timings->mem_clock == MEM_CLOCK_1067MT) ? 8 : 1;
208
209 rec_timing_t rec_timings[2][4] = {
210 {
211 { timings->CAS + 1, 0, 0, 0, t_bound, 0, p_bound },
212 { timings->CAS + 1, 0, 0, 0, t_bound, 0, p_bound },
213 { timings->CAS + 1, 0, 0, 0, t_bound, 0, p_bound },
214 { timings->CAS + 1, 0, 0, 0, t_bound, 0, p_bound }
215 }, {
216 { timings->CAS + 1, 0, 0, 0, t_bound, 0, p_bound },
217 { timings->CAS + 1, 0, 0, 0, t_bound, 0, p_bound },
218 { timings->CAS + 1, 0, 0, 0, t_bound, 0, p_bound },
219 { timings->CAS + 1, 0, 0, 0, t_bound, 0, p_bound }
220 }
221 };
222
223 int ch, group;
224 FOR_EACH_POPULATED_CHANNEL(dimms, ch) {
225 const char (*const map)[2] = over_bytelane_map[cardF[ch]];
226 for (group = 0; group < 4; ++group) {
227 program_timing(ch, group, rec_timings);
228 find_dqs_low(ch, group, rec_timings, map);
229 find_dqs_edge_lowhigh(ch, group, rec_timings, map);
230
231 rec_quarter_step(&rec_timings[ch][group]);
232 program_timing(ch, group, rec_timings);
233 find_preamble(ch, group, rec_timings, map);
234 find_dqs_edge_lowhigh(ch, group, rec_timings, map);
235 rec_half_backstep(&rec_timings[ch][group]);
236 normalize_rec_timing(&rec_timings[ch][group]);
237 if (cardF[ch]) {
238 rec_timings[ch][group].t++;
239 program_timing(ch, group, rec_timings);
240 }
241 }
242 int c_min = C_BOUND;
243 for (group = 0; group < 4; ++group) {
244 if (rec_timings[ch][group].c < c_min)
245 c_min = rec_timings[ch][group].c;
246 }
247 for (group = 0; group < 4; ++group) {
248 rec_timings[ch][group].pre =
249 rec_timings[ch][group].c - c_min;
250 rec_timings[ch][group].c = c_min;
251 program_timing(ch, group, rec_timings);
Nico Huber0624f922017-04-15 15:57:28 +0200252 printk(RAM_DEBUG, "Final timings for ");
253 printk(BIOS_DEBUG, "group %d, ch %d: %d.%d.%d.%d.%d\n",
Patrick Georgi2efc8802012-11-06 11:03:53 +0100254 group, ch,
255 rec_timings[ch][group].c,
256 rec_timings[ch][group].pre,
257 rec_timings[ch][group].ph,
258 rec_timings[ch][group].t,
259 rec_timings[ch][group].p);
260 }
261 }
262}
263
264void raminit_receive_enable_calibration(const timings_t *const timings,
265 const dimminfo_t *const dimms)
266{
267 int ch;
268
269 /* Setup group to byte-lane mapping. */
270 FOR_EACH_POPULATED_CHANNEL(dimms, ch) {
271 const char (*const map)[2] =
272 bytelane_map[dimms[ch].card_type == 0xf];
273 unsigned int group;
274 for (group = 0; group < 4; ++group) {
275 const unsigned int mchbar = CxRECy_MCHBAR(ch, group);
276 u32 reg = MCHBAR32(mchbar);
277 reg &= ~((3 << 16) | (1 << 8) | 3);
278 reg |= (map[group][0] - group);
279 reg |= (map[group][1] - group - 1) << 16;
280 MCHBAR32(mchbar) = reg;
281 }
282 }
283
284 MCHBAR32(0x12a4) |= 1 << 31;
285 MCHBAR32(0x13a4) |= 1 << 31;
286 MCHBAR32(0x14f0) = (MCHBAR32(0x14f0) & ~(3 << 9)) | (1 << 9);
287 MCHBAR32(0x15f0) = (MCHBAR32(0x15f0) & ~(3 << 9)) | (1 << 9);
288
289 receive_enable_calibration(timings, dimms);
290
291 MCHBAR32(0x12a4) &= ~(1 << 31);
292 MCHBAR32(0x13a4) &= ~(1 << 31);
293 raminit_reset_readwrite_pointers();
294}