blob: fdede0ab4670422e445549a57b8cf8b301bef9df [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
27#include "mosys/platform.h"
28#include "mosys/kv_pair.h"
29
30#include "lib/string.h"
31#include "lib/valstr.h"
32
33#include "lib/ddr3.h"
dhendrix@google.com7d320d22011-02-08 22:21:06 +000034#include "lib/spd.h"
35
dhendrix@google.com1f8bd822011-02-08 22:57:20 +000036#include "jedec_id.h"
37
dhendrix@google.com1b1f1d12011-02-12 00:01:04 +000038static const struct valstr ddr3_module_type_lut[] = {
39 { 0x00, "Undefined" },
40 { 0x01, "RDIMM" },
41 { 0x02, "UDIMM" },
42 { 0x03, "SO-DIMM" },
43 { 0x04, "MICRO-DIMM" },
44 { 0x05, "MINI-RDIMM" },
45 { 0x06, "MINI-UDIMM" },
46 { 0x07, "MINI-CDIMM" },
47 { 0x08, "72b-SO-UDIMM" },
48 { 0x09, "72b-SO-RDIMM" },
49 { 0x0a, "72b-SO-CDIMM" },
50 { 0x0b, "LRDIMM" },
51};
52
dhendrix@google.com7d320d22011-02-08 22:21:06 +000053/*
54 * spd_print_field_ddr3 - add common DDR SPD fields into key=value pair
55 *
56 * @intf: platform interface
57 * @kv: key=value pair
58 * @data: raw spd data
59 * @type: type of field to retrieve
60 *
61 * returns 1 to indicate data added to key=value pair
62 * returns 0 to indicate no data added
63 * returns <0 to indicate error
64 *
65 */
66int spd_print_field_ddr3(struct platform_intf *intf, struct kv_pair *kv,
67 const void *data, enum spd_field_type type)
68{
69 int ret;
70 const uint8_t *byte = data;
71
72 ret = 0;
73 switch (type) {
dhendrix@google.com1b1f1d12011-02-12 00:01:04 +000074 case SPD_GET_DRAM_TYPE:
dhendrix@google.com597d3032011-02-12 00:05:53 +000075 kv_pair_add(kv, "dram", "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
148 case SPD_GET_SERIAL_NUMBER:
149 {
dhendrix@google.comdac5b422011-02-09 02:43:16 +0000150 kv_pair_fmt(kv, "serial_number", "%02x%02x%02x%02x",
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000151 byte[DDR3_SPD_REG_MODULE_MANUF_SERIAL_0],
152 byte[DDR3_SPD_REG_MODULE_MANUF_SERIAL_1],
153 byte[DDR3_SPD_REG_MODULE_MANUF_SERIAL_2],
154 byte[DDR3_SPD_REG_MODULE_MANUF_SERIAL_3]);
155 ret = 1;
156 break;
157 }
158
159 case SPD_GET_PART_NUMBER:
160 {
161 char part[19];
162
163 memcpy(part, &byte[DDR3_SPD_REG_MODULE_PART_NUM_0], 18);
164 part[18] = '\0';
165 kv_pair_fmt(kv, "part_number", "%s", part);
166
167 ret = 1;
168 break;
169 }
170
171 case SPD_GET_REVISION_CODE:
172 {
173 kv_pair_fmt(kv, "revision_code", "0x%02x%02x",
174 byte[DDR3_SPD_REG_MODULE_REVISION_0],
175 byte[DDR3_SPD_REG_MODULE_REVISION_1]);
176 ret = 1;
177 break;
178 }
179
180 case SPD_GET_SIZE:
181 {
182 /* See "Calculating Module Capacity" section in DDR3 SPD
183 * specification for details. */
184 long size;
185
186 /* calculate the total size in MB */
187 size = 256 << (byte[DDR3_SPD_REG_DENSITY_BANKS] & 0xf);
188 size >>= 3; /* in terms of bytes instead of bits. */
189 size *= 8 << (byte[DDR3_SPD_REG_MODULE_BUS_WIDTH] & 0x7);
190 size /= 4 << (byte[DDR3_SPD_REG_MODULE_ORG] & 0x7);
191 size *= 1 + ((byte[DDR3_SPD_REG_MODULE_ORG] >> 3) & 0x7);
192
dhendrix@google.com52c2e8a2011-02-09 04:35:18 +0000193 kv_pair_fmt(kv, "size_mb", "%llu", size);
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000194 ret = 1;
195 break;
196 }
197
198 case SPD_GET_ECC:
199 {
200 uint8_t bus_ext_width = byte[DDR3_SPD_REG_MODULE_BUS_WIDTH];
201 bus_ext_width >>= 3;
202 bus_ext_width &= 0x7;
203 kv_pair_add_bool(kv, "ecc", bus_ext_width);
204 ret = 1;
205 break;
206 }
207
208 case SPD_GET_RANKS:
209 {
210 kv_pair_fmt(kv, "ranks", "%d",
211 1 + ((byte[DDR3_SPD_REG_MODULE_ORG] >> 3) & 0x7));
212 ret = 1;
213 break;
214 }
215
216 case SPD_GET_WIDTH:
217 {
218 /* Total width including ECC. */
219 uint8_t width;
220 width = 8 << (byte[DDR3_SPD_REG_MODULE_BUS_WIDTH] & 0x7);
221 width += 8 * ((byte[DDR3_SPD_REG_MODULE_BUS_WIDTH] >> 3) & 0x7);
222 kv_pair_fmt(kv, "width", "%d", width);
223 ret = 1;
224 break;
225 }
226
227 case SPD_GET_CHECKSUM:
228 {
229 kv_pair_fmt(kv, "checksum", "0x%02x%02x",
230 byte[DDR3_SPD_REG_CRC_1],
231 byte[DDR3_SPD_REG_CRC_0]);
232 ret = 1;
233 break;
234 }
235
236 case SPD_GET_SPEEDS:
237 {
238 int i;
239 int mhz;
240 int one_added;
241 char speeds[128];
242 const struct valstr possible_mhz[] = {
243 { 400, "DDR3-800" },
244 { 533, "DDR3-1066" },
245 { 667, "DDR3-1333" },
246 { 800, "DDR3-1600" },
247 { 0 }
248 };
249
250 mhz = 1000 * byte[DDR3_SPD_REG_MTB_DIVISOR];
251 mhz /= byte[DDR3_SPD_REG_MTB_DIVIDEND];
252 mhz /= byte[DDR3_SPD_REG_TCK_MIN];
253
254 memset(speeds, 0, sizeof(speeds));
255 one_added = 0;
256 for (i = 0; possible_mhz[i].val != 0; i++) {
dhendrix@google.com04cdd4f2011-09-21 22:20:32 +0000257 double min = possible_mhz[i].val * 0.99;
258
259 if (min <= mhz) {
dhendrix@google.com7d320d22011-02-08 22:21:06 +0000260 if (one_added) {
261 strcat(speeds, ", ");
262 }
263 one_added = 1;
264 strcat(speeds, possible_mhz[i].str);
265 }
266 }
267
268 kv_pair_add(kv, "speeds", speeds);
269 ret = 1;
270 break;
271 }
272
273 default:
274 {
275 ret = 0; /* force "we don't handle this here */
276 break;
277 }
278 }
279
280 return ret;
281}