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