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