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