coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
raminit_receive_enable_calibration.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <stdint.h>
4 #include <device/mmio.h>
5 #include <console/console.h>
6 #include "gm45.h"
7 
8 #define CxRECy_MCHBAR(x, y) (0x14a0 + ((x) * 0x0100) + ((3 - (y)) * 4))
9 #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)
15 #define CxRECy_T(t) (((t) << CxRECy_T_SHIFT) & CxRECy_T_MASK)
16 #define CxRECy_P_SHIFT 24
17 #define CxRECy_P_MASK (0x7 << CxRECy_P_SHIFT)
18 #define CxRECy_P(p) (((p) << CxRECy_P_SHIFT) & CxRECy_P_MASK)
19 #define CxRECy_PH_SHIFT 22
20 #define CxRECy_PH_MASK (0x3 << CxRECy_PH_SHIFT)
21 #define CxRECy_PH(p) (((p) << CxRECy_PH_SHIFT) & CxRECy_PH_MASK)
22 #define CxRECy_PM_SHIFT 20
23 #define CxRECy_PM_MASK (0x3 << CxRECy_PM_SHIFT)
24 #define CxRECy_PM(p) (((p) << CxRECy_PM_SHIFT) & CxRECy_PM_MASK)
25 #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)
30 #define CxDRT3_C(c) (((c) << CxDRT3_C_SHIFT) & CxDRT3_C_MASK)
31 /* group to byte-lane mapping: (cardF X group X 2 per group) */
32 static 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
41 typedef 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;
50 static 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 
81 static void rec_full_backstep(rec_timing_t *const timing)
82 {
83  timing->c--;
84 }
85 static void rec_half_backstep(rec_timing_t *const timing)
86 {
87  timing->ph -= PH_STEP;
88 }
89 static 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 }
94 static 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 }
99 static void rec_smallest_step(rec_timing_t *const timing)
100 {
101  timing->p++;
102 }
103 
104 static 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);
113  mchbar_clrsetbits32(mchbar, CxDRT3_C_MASK, CxDRT3_C(timing->c));
114 
115  /* All other per group. */
116  mchbar = CxRECy_MCHBAR(channel, group);
117  u32 reg = mchbar_read32(mchbar);
118  reg &= ~CxRECy_TIMING_MASK;
119  reg |= CxRECy_T(timing->t) | CxRECy_P(timing->p) |
120  CxRECy_PH(timing->ph) | CxRECy_PM(timing->pre);
121  mchbar_write32(mchbar, reg);
122 }
123 
124 static int read_dqs_level(const int channel, const int lane)
125 {
126  unsigned int mchbar = 0x14f0 + (channel * 0x0100);
127  mchbar_clrbits32(mchbar, 1 << 9);
128  mchbar_setbits32(mchbar, 1 << 9);
129 
130  /* Read from this channel. */
131  read32((u32 *)raminit_get_rank_addr(channel, 0));
132 
133  mchbar = 0x14b0 + (channel * 0x0100) + ((7 - lane) * 4);
134  return mchbar_read32(mchbar) & (1 << 30);
135 }
136 
137 static 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 }
147 static 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 }
157 static 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 }
177 static 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 
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 
197  const int cardF[] = {
198  dimms[0].card_type == 0xf,
199  dimms[1].card_type == 0xf,
200  };
201 
202  const unsigned int t_bound =
203  (timings->mem_clock == MEM_CLOCK_1067MT) ? 9 : 12;
204  const unsigned int p_bound =
205  (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;
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);
250  printk(RAM_DEBUG, "Final timings for ");
251  printk(BIOS_DEBUG, "group %d, ch %d: %d.%d.%d.%d.%d\n",
252  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 
263  const dimminfo_t *const dimms)
264 {
265  int ch;
266 
267  /* Setup group to byte-lane mapping. */
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);
274  u32 reg = mchbar_read32(mchbar);
275  reg &= ~((3 << 16) | (1 << 8) | 3);
276  reg |= (map[group][0] - group);
277  reg |= (map[group][1] - group - 1) << 16;
278  mchbar_write32(mchbar, reg);
279  }
280  }
281 
282  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);
286 
288 
289  mchbar_clrbits32(0x12a4, 1 << 31);
290  mchbar_clrbits32(0x13a4, 1 << 31);
292 }
static uint32_t read32(const void *addr)
Definition: mmio.h:22
#define printk(level,...)
Definition: stdlib.h:16
void __noreturn die(const char *fmt,...)
Definition: die.c:17
#define RAM_DEBUG
Definition: console.h:13
#define mchbar_setbits32(addr, set)
Definition: fixed_bars.h:58
static __always_inline void mchbar_write32(const uintptr_t offset, const uint32_t value)
Definition: fixed_bars.h:36
static __always_inline void mchbar_clrsetbits32(uintptr_t offset, uint32_t clear, uint32_t set)
Definition: fixed_bars.h:51
static __always_inline uint32_t mchbar_read32(const uintptr_t offset)
Definition: fixed_bars.h:21
#define mchbar_clrbits32(addr, clear)
Definition: fixed_bars.h:62
#define FOR_EACH_POPULATED_CHANNEL(dimms, idx)
Definition: gm45.h:138
#define CxDRT3_MCHBAR(x)
Definition: gm45.h:315
void raminit_reset_readwrite_pointers(void)
Definition: raminit.c:1677
@ MEM_CLOCK_1067MT
Definition: gm45.h:45
u32 raminit_get_rank_addr(unsigned int channel, unsigned int rank)
Definition: raminit.c:1660
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
static struct dramc_channel const ch[2]
static const char bytelane_map[2][4][2]
static void rec_quarter_backstep(rec_timing_t *const timing)
static void rec_quarter_step(rec_timing_t *const timing)
void raminit_receive_enable_calibration(const timings_t *const timings, const dimminfo_t *const dimms)
static void find_dqs_high(const int channel, const int group, rec_timing_t timings[][4], const char lane_map[][2])
#define CxRECy_MCHBAR(x, y)
static int read_dqs_level(const int channel, const int lane)
static void normalize_rec_timing(rec_timing_t *const timing)
static void find_preamble(const int channel, const int group, rec_timing_t timings[][4], const char lane_map[][2])
static void program_timing(int channel, int group, rec_timing_t timings[][4])
static void receive_enable_calibration(const timings_t *const timings, const dimminfo_t *const dimms)
#define CxRECy_PH(p)
#define CxRECy_TIMING_MASK
static void find_dqs_low(const int channel, const int group, rec_timing_t timings[][4], const char lane_map[][2])
static void find_dqs_edge_lowhigh(const int channel, const int group, rec_timing_t timings[][4], const char lane_map[][2])
static void rec_full_backstep(rec_timing_t *const timing)
static void rec_half_backstep(rec_timing_t *const timing)
static void rec_smallest_step(rec_timing_t *const timing)
#define CxRECy_PM(p)
uint32_t u32
Definition: stdint.h:51
unsigned int card_type
Definition: gm45.h:96
Definition: gm45.h:79
enum mem_clk mem_clock
Definition: raminit.h:47
unsigned int CAS
Definition: raminit.h:45
#define c(value, pmcreg, dst_bits)