blob: a66ee86fd15633d75e3bca1ba97afc069614c960 [file] [log] [blame]
Angel Ponsc74dae92020-04-02 23:48:16 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Andrey Petrov3f85edb2019-08-01 14:18:06 -07002
3#include <console/console.h>
4#include <cbmem.h>
5#include <device/device.h>
6#include <device/dram/ddr4.h>
7#include <string.h>
8#include <memory_info.h>
9#include <smbios.h>
10#include <types.h>
11
Rob Barnesa01ee362020-09-14 07:46:19 -060012enum ddr4_speed_grade {
13 DDR4_1600,
14 DDR4_1866,
15 DDR4_2133,
16 DDR4_2400,
17 DDR4_2666,
18 DDR4_2933,
19 DDR4_3200
20};
21
22struct ddr4_speed_attr {
23 uint32_t min_clock_mhz; // inclusive
24 uint32_t max_clock_mhz; // inclusive
25 uint32_t reported_mts;
26};
27
28/**
29 * DDR4 speed attributes derived from JEDEC 79-4C tables 169 & 170
30 *
31 * min_clock_mhz = 1000/max_tCk_avg(ns) + 1
32 * Adding 1 to make minimum inclusive
33 * max_clock_mhz = 1000/min_tCk_avg(ns)
34 * reported_mts = Standard reported DDR4 speed in MT/s
35 * May be 1 less than the actual max MT/s
36 */
37static const struct ddr4_speed_attr ddr4_speeds[] = {
38 [DDR4_1600] = {
39 .min_clock_mhz = 668,
40 .max_clock_mhz = 800,
41 .reported_mts = 1600
42 },
43 [DDR4_1866] = {
44 .min_clock_mhz = 801,
45 .max_clock_mhz = 934,
46 .reported_mts = 1866
47 },
48 [DDR4_2133] = {
49 .min_clock_mhz = 935,
50 .max_clock_mhz = 1067,
51 .reported_mts = 2133
52 },
53 [DDR4_2400] = {
54 .min_clock_mhz = 1068,
55 .max_clock_mhz = 1200,
56 .reported_mts = 2400
57 },
58 [DDR4_2666] = {
59 .min_clock_mhz = 1201,
60 .max_clock_mhz = 1333,
61 .reported_mts = 2666
62 },
63 [DDR4_2933] = {
64 .min_clock_mhz = 1334,
65 .max_clock_mhz = 1466,
66 .reported_mts = 2933
67 },
68 [DDR4_3200] = {
69 .min_clock_mhz = 1467,
70 .max_clock_mhz = 1600,
71 .reported_mts = 3200
72 }
73};
74
Andrey Petrov3f85edb2019-08-01 14:18:06 -070075typedef enum {
76 BLOCK_0, /* Base Configuration and DRAM Parameters */
77 BLOCK_1,
78 BLOCK_1_L, /* Standard Module Parameters */
79 BLOCK_1_H, /* Hybrid Module Parameters */
80 BLOCK_2,
81 BLOCK_2_L, /* Hybrid Module Extended Function Parameters */
82 BLOCK_2_H, /* Manufacturing Information */
83 BLOCK_3 /* End user programmable */
84} spd_block_type;
85
86typedef struct {
87 spd_block_type type;
88 uint16_t start; /* starting offset from beginning of the spd */
89 uint16_t len; /* size of the block */
90 uint16_t crc_start; /* offset from start of crc bytes, 0 if none */
91} spd_block;
92
93/* 'SPD contents architecture' as per datasheet */
94const spd_block spd_blocks[] = {
95 {.type = BLOCK_0, 0, 128, 126}, {.type = BLOCK_1, 128, 128, 126},
96 {.type = BLOCK_1_L, 128, 64, 0}, {.type = BLOCK_1_H, 192, 64, 0},
97 {.type = BLOCK_2_L, 256, 64, 62}, {.type = BLOCK_2_H, 320, 64, 0},
98 {.type = BLOCK_3, 384, 128, 0} };
99
100static bool verify_block(const spd_block *block, spd_raw_data spd)
101{
102 uint16_t crc, spd_crc;
103
104 spd_crc = (spd[block->start + block->crc_start + 1] << 8)
105 | spd[block->start + block->crc_start];
106 crc = ddr_crc16(&spd[block->start], block->len - 2);
107
108 return spd_crc == crc;
109}
110
111/* Check if given block is 'reserved' for a given module type */
112static bool block_exists(spd_block_type type, u8 dimm_type)
113{
114 bool is_hybrid;
115
116 switch (type) {
117 case BLOCK_0: /* fall-through */
118 case BLOCK_1: /* fall-through */
119 case BLOCK_1_L: /* fall-through */
120 case BLOCK_1_H: /* fall-through */
121 case BLOCK_2_H: /* fall-through */
122 case BLOCK_3: /* fall-through */
123 return true;
124 case BLOCK_2_L:
125 is_hybrid = (dimm_type >> 4) & ((1 << 3) - 1);
126 if (is_hybrid)
127 return true;
128 return false;
129 default: /* fall-through */
130 return false;
131 }
132}
133
Rob Barnesa01ee362020-09-14 07:46:19 -0600134/**
135 * Converts DDR4 clock speed in MHz to the standard reported speed in MT/s
136 */
137uint16_t ddr4_speed_mhz_to_reported_mts(uint16_t speed_mhz)
138{
139 for (enum ddr4_speed_grade speed = 0; speed < ARRAY_SIZE(ddr4_speeds); speed++) {
140 const struct ddr4_speed_attr *speed_attr = &ddr4_speeds[speed];
141 if (speed_mhz >= speed_attr->min_clock_mhz &&
142 speed_mhz <= speed_attr->max_clock_mhz) {
143 return speed_attr->reported_mts;
144 }
145 }
Julius Wernere9665952022-01-21 17:06:20 -0800146 printk(BIOS_ERR, "DDR4 speed of %d MHz is out of range\n", speed_mhz);
Rob Barnesa01ee362020-09-14 07:46:19 -0600147 return 0;
148}
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700149
150/**
151 * \brief Decode the raw SPD data
152 *
153 * Decodes a raw SPD data from a DDR4 DIMM, and organizes it into a
154 * @ref dimm_attr structure. The SPD data must first be read in a contiguous
155 * array, and passed to this function.
156 *
157 * @param dimm pointer to @ref dimm_attr structure where the decoded data is to
158 * be stored
159 * @param spd array of raw data previously read from the SPD.
160 *
161 * @return @ref spd_status enumerator
162 * SPD_STATUS_OK -- decoding was successful
163 * SPD_STATUS_INVALID -- invalid SPD or not a DDR4 SPD
164 * SPD_STATUS_CRC_ERROR -- checksum mismatch
165 */
Angel Pons6c6e0492021-03-28 13:57:47 +0200166int spd_decode_ddr4(struct dimm_attr_ddr4_st *dimm, spd_raw_data spd)
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700167{
168 u8 reg8;
169 u8 bus_width, sdram_width;
170 u16 cap_per_die_mbit;
171 u16 spd_bytes_total, spd_bytes_used;
172 const uint16_t spd_bytes_used_table[] = {0, 128, 256, 384, 512};
173
174 /* Make sure that the SPD dump is indeed from a DDR4 module */
175 if (spd[2] != SPD_MEMORY_TYPE_DDR4_SDRAM) {
176 printk(BIOS_ERR, "Not a DDR4 SPD!\n");
177 dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
178 return SPD_STATUS_INVALID;
179 }
180
Elyes HAOUAS2a0fbe32019-09-24 14:24:45 +0200181 spd_bytes_total = (spd[0] >> 4) & 0x7;
182 spd_bytes_used = spd[0] & 0xf;
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700183
184 if (!spd_bytes_total || !spd_bytes_used) {
185 printk(BIOS_ERR, "SPD failed basic sanity checks\n");
186 return SPD_STATUS_INVALID;
187 }
188
Elyes HAOUAS2a0fbe32019-09-24 14:24:45 +0200189 if (spd_bytes_total >= 3)
190 printk(BIOS_WARNING, "SPD Bytes Total value is reserved\n");
191
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700192 spd_bytes_total = 256 << (spd_bytes_total - 1);
Elyes HAOUAS2a0fbe32019-09-24 14:24:45 +0200193
194 if (spd_bytes_used > 4) {
195 printk(BIOS_ERR, "SPD Bytes Used value is reserved\n");
196 return SPD_STATUS_INVALID;
197 }
198
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700199 spd_bytes_used = spd_bytes_used_table[spd_bytes_used];
200
Elyes HAOUAS2a0fbe32019-09-24 14:24:45 +0200201 if (spd_bytes_used > spd_bytes_total) {
202 printk(BIOS_ERR, "SPD Bytes Used is greater than SPD Bytes Total\n");
203 return SPD_STATUS_INVALID;
204 }
205
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700206 /* Verify CRC of blocks that have them, do not step over 'used' length */
207 for (int i = 0; i < ARRAY_SIZE(spd_blocks); i++) {
Martin Roth0949e732021-10-01 14:28:22 -0600208 /* this block is not checksummed */
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700209 if (spd_blocks[i].crc_start == 0)
210 continue;
211 /* we shouldn't have this block */
212 if (spd_blocks[i].start + spd_blocks[i].len > spd_bytes_used)
213 continue;
214 /* check if block exists in the current schema */
215 if (!block_exists(spd_blocks[i].type, spd[3]))
216 continue;
217 if (!verify_block(&spd_blocks[i], spd)) {
218 printk(BIOS_ERR, "CRC failed for block %d\n", i);
219 return SPD_STATUS_CRC_ERROR;
220 }
221 }
222
223 dimm->dram_type = SPD_MEMORY_TYPE_DDR4_SDRAM;
224 dimm->dimm_type = spd[3] & ((1 << 4) - 1);
225
226 reg8 = spd[13] & ((1 << 4) - 1);
227 dimm->bus_width = reg8;
228 bus_width = 8 << (reg8 & ((1 << 3) - 1));
229
230 reg8 = spd[12] & ((1 << 3) - 1);
231 dimm->sdram_width = reg8;
232 sdram_width = 4 << reg8;
233
234 reg8 = spd[4] & ((1 << 4) - 1);
235 dimm->cap_per_die_mbit = reg8;
236 cap_per_die_mbit = (1 << reg8) * 256;
237
238 reg8 = (spd[12] >> 3) & ((1 << 3) - 1);
239 dimm->ranks = reg8 + 1;
240
241 if (!bus_width || !sdram_width) {
242 printk(BIOS_ERR, "SPD information is invalid");
243 dimm->size_mb = 0;
244 return SPD_STATUS_INVALID;
245 }
246
247 /* seems to be only one, in mV */
248 dimm->vdd_voltage = 1200;
249
250 /* calculate size */
251 dimm->size_mb = cap_per_die_mbit / 8 * bus_width / sdram_width * dimm->ranks;
252
253 /* make sure we have the manufacturing information block */
254 if (spd_bytes_used > 320) {
255 dimm->manufacturer_id = (spd[351] << 8) | spd[350];
256 memcpy(dimm->part_number, &spd[329], SPD_DDR4_PART_LEN);
257 dimm->part_number[SPD_DDR4_PART_LEN] = 0;
258 memcpy(dimm->serial_number, &spd[325], sizeof(dimm->serial_number));
259 }
260 return SPD_STATUS_OK;
261}
262
263enum cb_err spd_add_smbios17_ddr4(const u8 channel, const u8 slot, const u16 selected_freq,
Angel Pons6c6e0492021-03-28 13:57:47 +0200264 const struct dimm_attr_ddr4_st *info)
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700265{
266 struct memory_info *mem_info;
267 struct dimm_info *dimm;
268
269 /*
270 * Allocate CBMEM area for DIMM information used to populate SMBIOS
271 * table 17
272 */
273 mem_info = cbmem_find(CBMEM_ID_MEMINFO);
274 if (!mem_info) {
275 mem_info = cbmem_add(CBMEM_ID_MEMINFO, sizeof(*mem_info));
276
Julius Werner540a9802019-12-09 13:03:29 -0800277 printk(BIOS_DEBUG, "CBMEM entry for DIMM info: %p\n", mem_info);
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700278 if (!mem_info)
279 return CB_ERR;
280
281 memset(mem_info, 0, sizeof(*mem_info));
282 }
283
284 if (mem_info->dimm_cnt >= ARRAY_SIZE(mem_info->dimm)) {
285 printk(BIOS_WARNING, "BUG: Too many DIMM infos for %s.\n", __func__);
286 return CB_ERR;
287 }
288
289 dimm = &mem_info->dimm[mem_info->dimm_cnt];
290 if (info->size_mb) {
291 dimm->ddr_type = MEMORY_TYPE_DDR4;
292 dimm->ddr_frequency = selected_freq;
293 dimm->dimm_size = info->size_mb;
294 dimm->channel_num = channel;
295 dimm->rank_per_dimm = info->ranks;
296 dimm->dimm_num = slot;
297 memcpy(dimm->module_part_number, info->part_number, SPD_DDR4_PART_LEN);
298 dimm->mod_id = info->manufacturer_id;
299
300 switch (info->dimm_type) {
Angel Pons6c6e0492021-03-28 13:57:47 +0200301 case SPD_DDR4_DIMM_TYPE_SO_DIMM:
Subrata Banik6de8b422021-10-26 20:46:21 +0530302 dimm->mod_type = DDR4_SPD_SODIMM;
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700303 break;
Angel Pons6c6e0492021-03-28 13:57:47 +0200304 case SPD_DDR4_DIMM_TYPE_72B_SO_RDIMM:
Subrata Banik6de8b422021-10-26 20:46:21 +0530305 dimm->mod_type = DDR4_SPD_72B_SO_RDIMM;
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700306 break;
Angel Pons6c6e0492021-03-28 13:57:47 +0200307 case SPD_DDR4_DIMM_TYPE_UDIMM:
Subrata Banik6de8b422021-10-26 20:46:21 +0530308 dimm->mod_type = DDR4_SPD_UDIMM;
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700309 break;
Angel Pons6c6e0492021-03-28 13:57:47 +0200310 case SPD_DDR4_DIMM_TYPE_RDIMM:
Subrata Banik6de8b422021-10-26 20:46:21 +0530311 dimm->mod_type = DDR4_SPD_RDIMM;
Andrey Petrov3f85edb2019-08-01 14:18:06 -0700312 break;
313 default:
314 dimm->mod_type = SPD_UNDEFINED;
315 break;
316 }
317
318 dimm->bus_width = info->bus_width;
319 memcpy(dimm->serial, info->serial_number,
320 MIN(sizeof(dimm->serial), sizeof(info->serial_number)));
321
322 dimm->vdd_voltage = info->vdd_voltage;
323 mem_info->dimm_cnt++;
324 }
325
326 return CB_SUCCESS;
327}