blob: 818d6e67aee22861d61f68709700138fa76aeeb2 [file] [log] [blame]
davidhendricks@gmail.comffdc0932012-02-27 23:46:44 +00001/* Copyright 2012, Google Inc.
2 * All rights reserved.
dhendrix@google.com7d320d22011-02-08 22:21:06 +00003 *
davidhendricks@gmail.comffdc0932012-02-27 23:46:44 +00004 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
dhendrix@google.com7d320d22011-02-08 22:21:06 +00007 *
davidhendricks@gmail.comffdc0932012-02-27 23:46:44 +00008 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
dhendrix@google.com7d320d22011-02-08 22:21:06 +000017 *
davidhendricks@gmail.comffdc0932012-02-27 23:46:44 +000018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dhendrix@google.com7d320d22011-02-08 22:21:06 +000029 *
30 * DDR3 field access for DDR3 SPDs.
31 */
32
33#include <ctype.h>
34#include <stdint.h>
35#include <stdlib.h>
36#include <stdio.h>
37#include <string.h>
38
39#include "mosys/platform.h"
40#include "mosys/kv_pair.h"
David Hendricks7d51ea82014-04-01 17:22:58 -070041#include "mosys/log.h"
dhendrix@google.com7d320d22011-02-08 22:21:06 +000042
43#include "lib/string.h"
dhendrix@google.com7d320d22011-02-08 22:21:06 +000044
45#include "lib/ddr3.h"
dhendrix@google.com7d320d22011-02-08 22:21:06 +000046#include "lib/spd.h"
Jack Rosenthaldf9917b2020-10-21 10:57:39 -060047#include "lib/val2str.h"
dhendrix@google.com7d320d22011-02-08 22:21:06 +000048
dhendrix@google.com1f8bd822011-02-08 22:57:20 +000049#include "jedec_id.h"
50
dhendrix@google.com7d320d22011-02-08 22:21:06 +000051/*
52 * spd_print_field_ddr3 - add common DDR SPD fields into key=value pair
53 *
54 * @intf: platform interface
55 * @kv: key=value pair
56 * @data: raw spd data
57 * @type: type of field to retrieve
58 *
59 * returns 1 to indicate data added to key=value pair
60 * returns 0 to indicate no data added
61 * returns <0 to indicate error
62 *
63 */
64int spd_print_field_ddr3(struct platform_intf *intf, struct kv_pair *kv,
65 const void *data, enum spd_field_type type)
66{
67 int ret;
68 const uint8_t *byte = data;
69
70 ret = 0;
71 switch (type) {
dhendrix@google.com1b1f1d12011-02-12 00:01:04 +000072 case SPD_GET_DRAM_TYPE:
Duncan Laurie8f9820a2016-02-08 14:45:55 -080073 kv_pair_add(kv, "dram",
74 (byte[DDR3_SPD_REG_DEVICE_TYPE] ==
75 SPD_DRAM_TYPE_LPDDR3) ? "LPDDR3" : "DDR3");
dhendrix@google.com1b1f1d12011-02-12 00:01:04 +000076 ret = 1;
77 break;
78 case SPD_GET_MODULE_TYPE:
dhendrix@google.com597d3032011-02-12 00:05:53 +000079 kv_pair_add(kv, "module",
dhendrix@google.com1b1f1d12011-02-12 00:01:04 +000080 val2str(byte[DDR3_SPD_REG_MODULE_TYPE],
81 ddr3_module_type_lut));
82 ret = 1;
83 break;
dhendrix@google.com7d320d22011-02-08 22:21:06 +000084 case SPD_GET_MFG_ID:
85 {
86 uint8_t manuf_lsb;
87 uint8_t manuf_msb;
88 const char *tstr;
89
90 manuf_lsb = byte[DDR3_SPD_REG_MODULE_MANUF_JEDEC_ID_LSB] & 0x7f;
dhendrix@google.com2f6879c2011-02-09 04:29:50 +000091 manuf_msb = byte[DDR3_SPD_REG_MODULE_MANUF_JEDEC_ID_MSB] & 0x7f;
dhendrix@google.com7d320d22011-02-08 22:21:06 +000092
93 tstr = jedec_manufacturer(manuf_lsb, manuf_msb);
94
95 if (tstr != NULL) {
dhendrix@google.com00d74fc2011-02-09 03:02:11 +000096 kv_pair_fmt(kv, "module_mfg", "%u-%u: %s", manuf_lsb + 1,
dhendrix@google.com7d320d22011-02-08 22:21:06 +000097 manuf_msb, tstr);
98 } else {
dhendrix@google.com00d74fc2011-02-09 03:02:11 +000099 kv_pair_fmt(kv, "module_mfg", "%u-%u", manuf_lsb + 1,
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000100 manuf_msb);
101 }
102 ret = 1;
103 break;
104 }
105
106 case SPD_GET_MFG_ID_DRAM:
107 {
108 uint8_t manuf_lsb;
109 uint8_t manuf_msb;
110 const char *tstr;
111
112 manuf_lsb = byte[DDR3_SPD_REG_DRAM_MANUF_JEDEC_ID_LSB] & 0x7f;
dhendrix@google.com2f6879c2011-02-09 04:29:50 +0000113 manuf_msb = byte[DDR3_SPD_REG_DRAM_MANUF_JEDEC_ID_MSB] & 0x7f;
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000114
115 tstr = jedec_manufacturer(manuf_lsb, manuf_msb);
116
117 if (tstr != NULL) {
dhendrix@google.com00d74fc2011-02-09 03:02:11 +0000118 kv_pair_fmt(kv, "dram_mfg", "%u-%u: %s",
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000119 manuf_lsb + 1, manuf_msb, tstr);
120 } else {
dhendrix@google.com00d74fc2011-02-09 03:02:11 +0000121 kv_pair_fmt(kv, "dram_mfg", "%u-%u",
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000122 manuf_lsb + 1, manuf_msb);
123 }
124 ret = 1;
125 break;
126 }
127
128 case SPD_GET_MFG_LOC:
129 {
130 kv_pair_fmt(kv, "mfg_loc", "0x%02x",
131 byte[DDR3_SPD_REG_MODULE_MANUF_LOC]);
132 ret = 1;
133 break;
134 }
135
136 case SPD_GET_MFG_DATE: /* manufacturing date (BCD values) */
137 {
138 uint8_t year;
139 uint8_t week;
140
141 year = byte[DDR3_SPD_REG_MODULE_MANUF_DATE_YEAR];
142 week = byte[DDR3_SPD_REG_MODULE_MANUF_DATE_YEAR];
143 kv_pair_fmt(kv, "mfg_date", "20%02x-wk%02x", week, year);
144 ret = 1;
145 break;
146 }
147
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000148 case SPD_GET_PART_NUMBER:
149 {
150 char part[19];
151
152 memcpy(part, &byte[DDR3_SPD_REG_MODULE_PART_NUM_0], 18);
153 part[18] = '\0';
154 kv_pair_fmt(kv, "part_number", "%s", part);
155
156 ret = 1;
157 break;
158 }
159
160 case SPD_GET_REVISION_CODE:
161 {
162 kv_pair_fmt(kv, "revision_code", "0x%02x%02x",
163 byte[DDR3_SPD_REG_MODULE_REVISION_0],
164 byte[DDR3_SPD_REG_MODULE_REVISION_1]);
165 ret = 1;
166 break;
167 }
168
169 case SPD_GET_SIZE:
170 {
171 /* See "Calculating Module Capacity" section in DDR3 SPD
172 * specification for details. */
David Hendricksae2d5422012-07-18 14:52:41 -0700173 unsigned int size;
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000174
175 /* calculate the total size in MB */
176 size = 256 << (byte[DDR3_SPD_REG_DENSITY_BANKS] & 0xf);
177 size >>= 3; /* in terms of bytes instead of bits. */
178 size *= 8 << (byte[DDR3_SPD_REG_MODULE_BUS_WIDTH] & 0x7);
179 size /= 4 << (byte[DDR3_SPD_REG_MODULE_ORG] & 0x7);
180 size *= 1 + ((byte[DDR3_SPD_REG_MODULE_ORG] >> 3) & 0x7);
181
David Hendricksae2d5422012-07-18 14:52:41 -0700182 kv_pair_fmt(kv, "size_mb", "%u", size);
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000183 ret = 1;
184 break;
185 }
186
187 case SPD_GET_ECC:
188 {
189 uint8_t bus_ext_width = byte[DDR3_SPD_REG_MODULE_BUS_WIDTH];
190 bus_ext_width >>= 3;
191 bus_ext_width &= 0x7;
192 kv_pair_add_bool(kv, "ecc", bus_ext_width);
193 ret = 1;
194 break;
195 }
196
197 case SPD_GET_RANKS:
198 {
199 kv_pair_fmt(kv, "ranks", "%d",
200 1 + ((byte[DDR3_SPD_REG_MODULE_ORG] >> 3) & 0x7));
201 ret = 1;
202 break;
203 }
204
205 case SPD_GET_WIDTH:
206 {
207 /* Total width including ECC. */
208 uint8_t width;
209 width = 8 << (byte[DDR3_SPD_REG_MODULE_BUS_WIDTH] & 0x7);
210 width += 8 * ((byte[DDR3_SPD_REG_MODULE_BUS_WIDTH] >> 3) & 0x7);
211 kv_pair_fmt(kv, "width", "%d", width);
212 ret = 1;
213 break;
214 }
215
216 case SPD_GET_CHECKSUM:
217 {
218 kv_pair_fmt(kv, "checksum", "0x%02x%02x",
219 byte[DDR3_SPD_REG_CRC_1],
220 byte[DDR3_SPD_REG_CRC_0]);
221 ret = 1;
222 break;
223 }
224
225 case SPD_GET_SPEEDS:
226 {
David Hendricks7d51ea82014-04-01 17:22:58 -0700227 int i, mhz, first_entry;
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000228 char speeds[128];
229 const struct valstr possible_mhz[] = {
David Hendricks4f5033a2013-04-03 14:10:19 -0700230 { 400, "DDR3-800" },
231 { 533, "DDR3-1066" },
232 { 667, "DDR3-1333" },
233 { 800, "DDR3-1600" },
234 { 933, "DDR3-1866" },
235 { 1067, "DDR3-2133" },
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000236 { 0 }
237 };
David Hendricks7d51ea82014-04-01 17:22:58 -0700238 int tck_mtb = byte[DDR3_SPD_REG_TCK_MIN];
239 int mtb_dividend = byte[DDR3_SPD_REG_MTB_DIVIDEND];
240 int mtb_divisor = byte[DDR3_SPD_REG_MTB_DIVISOR];
241 int ftb_dividend = byte[DDR3_SPD_REG_FTB_DIVIDEND_DIVSOR] >> 4;
242 int ftb_divisor = byte[DDR3_SPD_REG_FTB_DIVIDEND_DIVSOR] & 0xf;
243 double tck_ns, mtb = 0.0, ftb_ns = 0.0;
244 /* fine offset is encoded in 2's complement format */
245 int8_t ftb_offset = byte[DDR3_SPD_REG_FINE_OFFSET_TCK_MIN];
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000246
David Hendricks7d51ea82014-04-01 17:22:58 -0700247 /* Sanity check that MTB and FTB values are >=1 (as per spec) */
248 ret = -1;
249 if (!mtb_dividend)
250 lprintf(LOG_ERR, "Invalid MTB dividend from SPD\n");
251 else if (!mtb_divisor)
252 lprintf(LOG_ERR, "Invalid MTB divisor from SPD\n");
253 else if (!ftb_dividend)
254 lprintf(LOG_ERR, "Invalid FTB dividend from SPD\n");
255 else if (!ftb_divisor)
256 lprintf(LOG_ERR, "Invalid FTB divisor from SPD\n");
257 else
258 ret = 0;
259 if (ret)
260 break;
261
262 mtb = (double)mtb_dividend / mtb_divisor;
263 ftb_ns = ((double)(ftb_dividend) / ftb_divisor) / 1000;
264 tck_ns = tck_mtb * mtb + (ftb_offset * ftb_ns);
265 mhz = (int)((double)1000/tck_ns);
266
267 lprintf(LOG_DEBUG, "%s: %d * %.03fns + %d * %.03fns = %.02fns,"
268 " mhz = %d\n", __func__,
269 tck_mtb, mtb, ftb_offset, ftb_ns, tck_ns, mhz);
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000270
271 memset(speeds, 0, sizeof(speeds));
David Hendricks7d51ea82014-04-01 17:22:58 -0700272 first_entry = 1;
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000273 for (i = 0; possible_mhz[i].val != 0; i++) {
dhendrix@google.com04cdd4f2011-09-21 22:20:32 +0000274 double min = possible_mhz[i].val * 0.99;
275
276 if (min <= mhz) {
David Hendricks7d51ea82014-04-01 17:22:58 -0700277 if (!first_entry)
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000278 strcat(speeds, ", ");
David Hendricks7d51ea82014-04-01 17:22:58 -0700279 first_entry = 0;
Duncan Laurie8f9820a2016-02-08 14:45:55 -0800280 if (byte[DDR3_SPD_REG_DEVICE_TYPE] ==
281 SPD_DRAM_TYPE_LPDDR3)
282 strcat(speeds, "LP");
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000283 strcat(speeds, possible_mhz[i].str);
284 }
285 }
286
287 kv_pair_add(kv, "speeds", speeds);
288 ret = 1;
289 break;
290 }
291
292 default:
293 {
294 ret = 0; /* force "we don't handle this here */
295 break;
296 }
297 }
298
299 return ret;
300}