blob: a0266ef8268e83acb27972eb89144ee4ca5c3c93 [file] [log] [blame]
dhendrix@google.com7d320d22011-02-08 22:21:06 +00001/*
2 * Copyright (C) 2011 Google Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 * DDR3 field access for DDR3 SPDs.
19 */
20
21#include <ctype.h>
22#include <stdint.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26
dhendrix@google.comcc628f52011-09-28 01:20:37 +000027#include <valstr.h>
28
dhendrix@google.com7d320d22011-02-08 22:21:06 +000029#include "mosys/platform.h"
30#include "mosys/kv_pair.h"
31
32#include "lib/string.h"
dhendrix@google.com7d320d22011-02-08 22:21:06 +000033
34#include "lib/ddr3.h"
dhendrix@google.com7d320d22011-02-08 22:21:06 +000035#include "lib/spd.h"
36
dhendrix@google.com1f8bd822011-02-08 22:57:20 +000037#include "jedec_id.h"
38
dhendrix@google.com1b1f1d12011-02-12 00:01:04 +000039static const struct valstr ddr3_module_type_lut[] = {
40 { 0x00, "Undefined" },
41 { 0x01, "RDIMM" },
42 { 0x02, "UDIMM" },
43 { 0x03, "SO-DIMM" },
44 { 0x04, "MICRO-DIMM" },
45 { 0x05, "MINI-RDIMM" },
46 { 0x06, "MINI-UDIMM" },
47 { 0x07, "MINI-CDIMM" },
48 { 0x08, "72b-SO-UDIMM" },
49 { 0x09, "72b-SO-RDIMM" },
50 { 0x0a, "72b-SO-CDIMM" },
51 { 0x0b, "LRDIMM" },
52};
53
dhendrix@google.com7d320d22011-02-08 22:21:06 +000054/*
55 * spd_print_field_ddr3 - add common DDR SPD fields into key=value pair
56 *
57 * @intf: platform interface
58 * @kv: key=value pair
59 * @data: raw spd data
60 * @type: type of field to retrieve
61 *
62 * returns 1 to indicate data added to key=value pair
63 * returns 0 to indicate no data added
64 * returns <0 to indicate error
65 *
66 */
67int spd_print_field_ddr3(struct platform_intf *intf, struct kv_pair *kv,
68 const void *data, enum spd_field_type type)
69{
70 int ret;
71 const uint8_t *byte = data;
72
73 ret = 0;
74 switch (type) {
dhendrix@google.com1b1f1d12011-02-12 00:01:04 +000075 case SPD_GET_DRAM_TYPE:
dhendrix@google.com597d3032011-02-12 00:05:53 +000076 kv_pair_add(kv, "dram", "DDR3");
dhendrix@google.com1b1f1d12011-02-12 00:01:04 +000077 ret = 1;
78 break;
79 case SPD_GET_MODULE_TYPE:
dhendrix@google.com597d3032011-02-12 00:05:53 +000080 kv_pair_add(kv, "module",
dhendrix@google.com1b1f1d12011-02-12 00:01:04 +000081 val2str(byte[DDR3_SPD_REG_MODULE_TYPE],
82 ddr3_module_type_lut));
83 ret = 1;
84 break;
dhendrix@google.com7d320d22011-02-08 22:21:06 +000085 case SPD_GET_MFG_ID:
86 {
87 uint8_t manuf_lsb;
88 uint8_t manuf_msb;
89 const char *tstr;
90
91 manuf_lsb = byte[DDR3_SPD_REG_MODULE_MANUF_JEDEC_ID_LSB] & 0x7f;
dhendrix@google.com2f6879c2011-02-09 04:29:50 +000092 manuf_msb = byte[DDR3_SPD_REG_MODULE_MANUF_JEDEC_ID_MSB] & 0x7f;
dhendrix@google.com7d320d22011-02-08 22:21:06 +000093
94 tstr = jedec_manufacturer(manuf_lsb, manuf_msb);
95
96 if (tstr != NULL) {
dhendrix@google.com00d74fc2011-02-09 03:02:11 +000097 kv_pair_fmt(kv, "module_mfg", "%u-%u: %s", manuf_lsb + 1,
dhendrix@google.com7d320d22011-02-08 22:21:06 +000098 manuf_msb, tstr);
99 } else {
dhendrix@google.com00d74fc2011-02-09 03:02:11 +0000100 kv_pair_fmt(kv, "module_mfg", "%u-%u", manuf_lsb + 1,
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000101 manuf_msb);
102 }
103 ret = 1;
104 break;
105 }
106
107 case SPD_GET_MFG_ID_DRAM:
108 {
109 uint8_t manuf_lsb;
110 uint8_t manuf_msb;
111 const char *tstr;
112
113 manuf_lsb = byte[DDR3_SPD_REG_DRAM_MANUF_JEDEC_ID_LSB] & 0x7f;
dhendrix@google.com2f6879c2011-02-09 04:29:50 +0000114 manuf_msb = byte[DDR3_SPD_REG_DRAM_MANUF_JEDEC_ID_MSB] & 0x7f;
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000115
116 tstr = jedec_manufacturer(manuf_lsb, manuf_msb);
117
118 if (tstr != NULL) {
dhendrix@google.com00d74fc2011-02-09 03:02:11 +0000119 kv_pair_fmt(kv, "dram_mfg", "%u-%u: %s",
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000120 manuf_lsb + 1, manuf_msb, tstr);
121 } else {
dhendrix@google.com00d74fc2011-02-09 03:02:11 +0000122 kv_pair_fmt(kv, "dram_mfg", "%u-%u",
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000123 manuf_lsb + 1, manuf_msb);
124 }
125 ret = 1;
126 break;
127 }
128
129 case SPD_GET_MFG_LOC:
130 {
131 kv_pair_fmt(kv, "mfg_loc", "0x%02x",
132 byte[DDR3_SPD_REG_MODULE_MANUF_LOC]);
133 ret = 1;
134 break;
135 }
136
137 case SPD_GET_MFG_DATE: /* manufacturing date (BCD values) */
138 {
139 uint8_t year;
140 uint8_t week;
141
142 year = byte[DDR3_SPD_REG_MODULE_MANUF_DATE_YEAR];
143 week = byte[DDR3_SPD_REG_MODULE_MANUF_DATE_YEAR];
144 kv_pair_fmt(kv, "mfg_date", "20%02x-wk%02x", week, year);
145 ret = 1;
146 break;
147 }
148
149 case SPD_GET_SERIAL_NUMBER:
150 {
dhendrix@google.comdac5b422011-02-09 02:43:16 +0000151 kv_pair_fmt(kv, "serial_number", "%02x%02x%02x%02x",
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000152 byte[DDR3_SPD_REG_MODULE_MANUF_SERIAL_0],
153 byte[DDR3_SPD_REG_MODULE_MANUF_SERIAL_1],
154 byte[DDR3_SPD_REG_MODULE_MANUF_SERIAL_2],
155 byte[DDR3_SPD_REG_MODULE_MANUF_SERIAL_3]);
156 ret = 1;
157 break;
158 }
159
160 case SPD_GET_PART_NUMBER:
161 {
162 char part[19];
163
164 memcpy(part, &byte[DDR3_SPD_REG_MODULE_PART_NUM_0], 18);
165 part[18] = '\0';
166 kv_pair_fmt(kv, "part_number", "%s", part);
167
168 ret = 1;
169 break;
170 }
171
172 case SPD_GET_REVISION_CODE:
173 {
174 kv_pair_fmt(kv, "revision_code", "0x%02x%02x",
175 byte[DDR3_SPD_REG_MODULE_REVISION_0],
176 byte[DDR3_SPD_REG_MODULE_REVISION_1]);
177 ret = 1;
178 break;
179 }
180
181 case SPD_GET_SIZE:
182 {
183 /* See "Calculating Module Capacity" section in DDR3 SPD
184 * specification for details. */
185 long size;
186
187 /* calculate the total size in MB */
188 size = 256 << (byte[DDR3_SPD_REG_DENSITY_BANKS] & 0xf);
189 size >>= 3; /* in terms of bytes instead of bits. */
190 size *= 8 << (byte[DDR3_SPD_REG_MODULE_BUS_WIDTH] & 0x7);
191 size /= 4 << (byte[DDR3_SPD_REG_MODULE_ORG] & 0x7);
192 size *= 1 + ((byte[DDR3_SPD_REG_MODULE_ORG] >> 3) & 0x7);
193
dhendrix@google.com52c2e8a2011-02-09 04:35:18 +0000194 kv_pair_fmt(kv, "size_mb", "%llu", size);
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000195 ret = 1;
196 break;
197 }
198
199 case SPD_GET_ECC:
200 {
201 uint8_t bus_ext_width = byte[DDR3_SPD_REG_MODULE_BUS_WIDTH];
202 bus_ext_width >>= 3;
203 bus_ext_width &= 0x7;
204 kv_pair_add_bool(kv, "ecc", bus_ext_width);
205 ret = 1;
206 break;
207 }
208
209 case SPD_GET_RANKS:
210 {
211 kv_pair_fmt(kv, "ranks", "%d",
212 1 + ((byte[DDR3_SPD_REG_MODULE_ORG] >> 3) & 0x7));
213 ret = 1;
214 break;
215 }
216
217 case SPD_GET_WIDTH:
218 {
219 /* Total width including ECC. */
220 uint8_t width;
221 width = 8 << (byte[DDR3_SPD_REG_MODULE_BUS_WIDTH] & 0x7);
222 width += 8 * ((byte[DDR3_SPD_REG_MODULE_BUS_WIDTH] >> 3) & 0x7);
223 kv_pair_fmt(kv, "width", "%d", width);
224 ret = 1;
225 break;
226 }
227
228 case SPD_GET_CHECKSUM:
229 {
230 kv_pair_fmt(kv, "checksum", "0x%02x%02x",
231 byte[DDR3_SPD_REG_CRC_1],
232 byte[DDR3_SPD_REG_CRC_0]);
233 ret = 1;
234 break;
235 }
236
237 case SPD_GET_SPEEDS:
238 {
239 int i;
240 int mhz;
241 int one_added;
242 char speeds[128];
243 const struct valstr possible_mhz[] = {
244 { 400, "DDR3-800" },
245 { 533, "DDR3-1066" },
246 { 667, "DDR3-1333" },
247 { 800, "DDR3-1600" },
248 { 0 }
249 };
250
251 mhz = 1000 * byte[DDR3_SPD_REG_MTB_DIVISOR];
252 mhz /= byte[DDR3_SPD_REG_MTB_DIVIDEND];
253 mhz /= byte[DDR3_SPD_REG_TCK_MIN];
254
255 memset(speeds, 0, sizeof(speeds));
256 one_added = 0;
257 for (i = 0; possible_mhz[i].val != 0; i++) {
dhendrix@google.com04cdd4f2011-09-21 22:20:32 +0000258 double min = possible_mhz[i].val * 0.99;
259
260 if (min <= mhz) {
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000261 if (one_added) {
262 strcat(speeds, ", ");
263 }
264 one_added = 1;
265 strcat(speeds, possible_mhz[i].str);
266 }
267 }
268
269 kv_pair_add(kv, "speeds", speeds);
270 ret = 1;
271 break;
272 }
273
274 default:
275 {
276 ret = 0; /* force "we don't handle this here */
277 break;
278 }
279 }
280
281 return ret;
282}