summaryrefslogtreecommitdiff
path: root/config/coreboot/haswell/patches/0012-haswell-NRI-Add-function-to-change-margins.patch
blob: f4f5161e52eb33174cd8f988cb3dac1c516878c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
From c24b26594bfab47a8709ed7fb5cb77307fb73a53 Mon Sep 17 00:00:00 2001
From: Angel Pons <th3fanbus@gmail.com>
Date: Sun, 8 May 2022 11:58:59 +0200
Subject: [PATCH 12/20] haswell NRI: Add function to change margins

Implement a function to change margin parameters. Haswell provides a
register to apply an offset to margin parameters during training, so
make use of it. There are other margin parameters that have not been
implemented yet, as they are not needed for now and special handling
is needed to provide offset training functionality.

Change-Id: I5392380e13de3c44e77b7bc9f3b819e2661d1e2d
Signed-off-by: Angel Pons <th3fanbus@gmail.com>
---
 .../haswell/native_raminit/change_margin.c    | 136 ++++++++++++++++++
 .../haswell/native_raminit/raminit_native.h   |  39 +++++
 .../haswell/native_raminit/reg_structs.h      |  12 ++
 .../intel/haswell/registers/mchbar.h          |   1 +
 4 files changed, 188 insertions(+)

diff --git a/src/northbridge/intel/haswell/native_raminit/change_margin.c b/src/northbridge/intel/haswell/native_raminit/change_margin.c
index 055c666eee..299c44a6b0 100644
--- a/src/northbridge/intel/haswell/native_raminit/change_margin.c
+++ b/src/northbridge/intel/haswell/native_raminit/change_margin.c
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 
+#include <assert.h>
 #include <commonlib/bsd/clamp.h>
 #include <console/console.h>
 #include <delay.h>
@@ -152,3 +153,138 @@ void download_regfile(
 	ddr_data_control_0.read_rf_rank = phys_rank;
 	mchbar_write32(reg, ddr_data_control_0.raw);
 }
+
+static void update_data_offset_train(
+	struct sysinfo *ctrl,
+	const uint8_t param,
+	const uint8_t en_multicast,
+	const uint8_t channel_in,
+	const uint8_t rank,
+	const uint8_t byte_in,
+	const bool update_ctrl,
+	const enum regfile_mode regfile,
+	const uint32_t value)
+{
+	bool is_rd = false;
+	bool is_wr = false;
+	switch (param) {
+	case RdT:
+	case RdV:
+	case RcvEna:
+		is_rd = true;
+		break;
+	case WrT:
+	case WrDqsT:
+		is_wr = true;
+		break;
+	default:
+		die("%s: Invalid margin parameter %u\n", __func__, param);
+	}
+	if (en_multicast) {
+		mchbar_write32(DDR_DATA_OFFSET_TRAIN, value);
+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
+			if (!does_ch_exist(ctrl, channel))
+				continue;
+
+			download_regfile(ctrl, channel, true, rank, regfile, 0, is_rd, is_wr);
+			if (update_ctrl) {
+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++)
+					ctrl->data_offset_train[channel][byte] = value;
+			}
+		}
+	} else {
+		mchbar_write32(DDR_DATA_OFFSET_TRAIN_ch_b(channel_in, byte_in), value);
+		download_regfile(ctrl, channel_in, false, rank, regfile, byte_in, is_rd, is_wr);
+		if (update_ctrl)
+			ctrl->data_offset_train[channel_in][byte_in] = value;
+	}
+}
+
+static uint32_t get_max_margin(const enum margin_parameter param)
+{
+	switch (param) {
+	case RcvEna:
+	case RdT:
+	case WrT:
+	case WrDqsT:
+		return MAX_POSSIBLE_TIME;
+	case RdV:
+		return MAX_POSSIBLE_VREF;
+	default:
+		die("%s: Invalid margin parameter %u\n", __func__, param);
+	}
+}
+
+void change_margin(
+	struct sysinfo *ctrl,
+	const enum margin_parameter param,
+	const int32_t value0,
+	const bool en_multicast,
+	const uint8_t channel,
+	const uint8_t rank,
+	const uint8_t byte,
+	const bool update_ctrl,
+	const enum regfile_mode regfile)
+{
+	/** FIXME: Remove this **/
+	if (rank == 0xff)
+		die("%s: rank is 0xff\n", __func__);
+
+	if (!en_multicast && !does_ch_exist(ctrl, channel))
+		die("%s: Tried to change margin of empty channel %u\n", __func__, channel);
+
+	const uint32_t max_value = get_max_margin(param);
+	const int32_t v0 = clamp_s32(-max_value, value0, max_value);
+
+	union ddr_data_offset_train_reg ddr_data_offset_train = {
+		.raw = en_multicast ? 0 : ctrl->data_offset_train[channel][byte],
+	};
+	bool update_offset_train = false;
+	switch (param) {
+	case RcvEna:
+		ddr_data_offset_train.rcven = v0;
+		update_offset_train = true;
+		break;
+	case RdT:
+		ddr_data_offset_train.rx_dqs = v0;
+		update_offset_train = true;
+		break;
+	case WrT:
+		ddr_data_offset_train.tx_dq = v0;
+		update_offset_train = true;
+		break;
+	case WrDqsT:
+		ddr_data_offset_train.tx_dqs = v0;
+		update_offset_train = true;
+		break;
+	case RdV:
+		ddr_data_offset_train.vref = v0;
+		update_offset_train = true;
+		break;
+	default:
+		die("%s: Invalid margin parameter %u\n", __func__, param);
+	}
+	if (update_offset_train) {
+		update_data_offset_train(
+			ctrl,
+			param,
+			en_multicast,
+			channel,
+			rank,
+			byte,
+			update_ctrl,
+			regfile,
+			ddr_data_offset_train.raw);
+	}
+}
+
+void change_1d_margin_multicast(
+	struct sysinfo *ctrl,
+	const enum margin_parameter param,
+	const int32_t value0,
+	const uint8_t rank,
+	const bool update_ctrl,
+	const enum regfile_mode regfile)
+{
+	change_margin(ctrl, param, value0, true, 0, rank, 0, update_ctrl, regfile);
+}
diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
index b4e8c7de5a..5242b16f28 100644
--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
@@ -35,6 +35,18 @@
 
 #define RTTNOM_MASK		(BIT(9) | BIT(6) | BIT(2))
 
+/* Margin parameter limits */
+#define MAX_POSSIBLE_TIME	31
+#define MAX_POSSIBLE_VREF	54
+
+#define MAX_POSSIBLE_BOTH	MAX_POSSIBLE_VREF
+
+#define MIN_TIME		(-MAX_POSSIBLE_TIME)
+#define MAX_TIME		(MAX_POSSIBLE_TIME)
+
+#define MIN_VREF		(-MAX_POSSIBLE_VREF)
+#define MAX_VREF		(MAX_POSSIBLE_VREF)
+
 #define BASIC_VA_PAT_SPREAD_8	0x01010101
 
 #define WDB_CACHE_LINE_SIZE	8
@@ -45,6 +57,14 @@
 /* Specified in PI ticks. 64 PI ticks == 1 qclk */
 #define tDQSCK_DRIFT		64
 
+enum margin_parameter {
+	RcvEna,
+	RdT,
+	WrT,
+	WrDqsT,
+	RdV,
+};
+
 /* ZQ calibration types */
 enum {
 	ZQ_INIT,	/* DDR3: ZQCL with tZQinit, LPDDR3: ZQ Init  with tZQinit  */
@@ -514,6 +534,25 @@ void download_regfile(
 	bool read_rf_rd,
 	bool read_rf_wr);
 
+void change_margin(
+	struct sysinfo *ctrl,
+	const enum margin_parameter param,
+	const int32_t value0,
+	const bool en_multicast,
+	const uint8_t channel,
+	const uint8_t rank,
+	const uint8_t byte,
+	const bool update_ctrl,
+	const enum regfile_mode regfile);
+
+void change_1d_margin_multicast(
+	struct sysinfo *ctrl,
+	const enum margin_parameter param,
+	const int32_t value0,
+	const uint8_t rank,
+	const bool update_ctrl,
+	const enum regfile_mode regfile);
+
 uint8_t get_rx_bias(const struct sysinfo *ctrl);
 
 uint8_t get_tCWL(uint32_t mem_clock_mhz);
diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h
index b099f4bb82..a0e36ed082 100644
--- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h
+++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h
@@ -25,6 +25,18 @@ union ddr_data_tx_train_rank_reg {
 	uint32_t raw;
 };
 
+union ddr_data_offset_train_reg {
+	struct __packed {
+		int32_t rcven  : 6; // Bits  5:0
+		int32_t rx_dqs : 6; // Bits 11:6
+		int32_t tx_dq  : 6; // Bits 17:12
+		int32_t tx_dqs : 6; // Bits 23:18
+		int32_t vref   : 7; // Bits 30:24
+		int32_t        : 1; // Bits 31:31
+	};
+	uint32_t raw;
+};
+
 union ddr_data_control_0_reg {
 	struct __packed {
 		uint32_t rx_training_mode      : 1; // Bits  0:0
diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h
index 9172d4f2b0..0acafbc826 100644
--- a/src/northbridge/intel/haswell/registers/mchbar.h
+++ b/src/northbridge/intel/haswell/registers/mchbar.h
@@ -21,6 +21,7 @@
 #define DDR_DATA_TRAIN_FEEDBACK(ch, byte)	_DDRIO_C_R_B(0x0054, ch, 0, byte)
 
 #define DQ_CONTROL_2(ch, byte)			_DDRIO_C_R_B(0x0064, ch, 0, byte)
+#define DDR_DATA_OFFSET_TRAIN_ch_b(ch, byte)	_DDRIO_C_R_B(0x0070, ch, 0, byte)
 #define DQ_CONTROL_0(ch, byte)			_DDRIO_C_R_B(0x0074, ch, 0, byte)
 
 /* DDR CKE per-channel */
-- 
2.39.2