1*b7d4deb4Skettenis /* $OpenBSD: spdmem.c,v 1.7 2019/12/21 12:33:03 kettenis Exp $ */
2b95f7d00Smiod /* $NetBSD: spdmem.c,v 1.3 2007/09/20 23:09:59 xtraeme Exp $ */
3b95f7d00Smiod
4b95f7d00Smiod /*
5b95f7d00Smiod * Copyright (c) 2007 Jonathan Gray <jsg@openbsd.org>
6b95f7d00Smiod *
7b95f7d00Smiod * Permission to use, copy, modify, and distribute this software for any
8b95f7d00Smiod * purpose with or without fee is hereby granted, provided that the above
9b95f7d00Smiod * copyright notice and this permission notice appear in all copies.
10b95f7d00Smiod *
11b95f7d00Smiod * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12b95f7d00Smiod * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13b95f7d00Smiod * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14b95f7d00Smiod * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15b95f7d00Smiod * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16b95f7d00Smiod * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17b95f7d00Smiod * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18b95f7d00Smiod */
19b95f7d00Smiod
20b95f7d00Smiod /*
21b95f7d00Smiod * Copyright (c) 2007 Nicolas Joly
22b95f7d00Smiod * Copyright (c) 2007 Paul Goyette
23b95f7d00Smiod * Copyright (c) 2007 Tobias Nygren
24b95f7d00Smiod * All rights reserved.
25b95f7d00Smiod *
26b95f7d00Smiod * Redistribution and use in source and binary forms, with or without
27b95f7d00Smiod * modification, are permitted provided that the following conditions
28b95f7d00Smiod * are met:
29b95f7d00Smiod * 1. Redistributions of source code must retain the above copyright
30b95f7d00Smiod * notice, this list of conditions and the following disclaimer.
31b95f7d00Smiod * 2. Redistributions in binary form must reproduce the above copyright
32b95f7d00Smiod * notice, this list of conditions and the following disclaimer in the
33b95f7d00Smiod * documentation and/or other materials provided with the distribution.
34b95f7d00Smiod * 3. The name of the author may not be used to endorse or promote products
35b95f7d00Smiod * derived from this software without specific prior written permission.
36b95f7d00Smiod *
37b95f7d00Smiod * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS
38b95f7d00Smiod * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
39b95f7d00Smiod * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
40b95f7d00Smiod * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
41b95f7d00Smiod * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42b95f7d00Smiod * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43b95f7d00Smiod * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
44b95f7d00Smiod * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
45b95f7d00Smiod * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46b95f7d00Smiod * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
47b95f7d00Smiod * POSSIBILITY OF SUCH DAMAGE.
48b95f7d00Smiod */
49b95f7d00Smiod
50b95f7d00Smiod /*
51b95f7d00Smiod * Serial Presence Detect (SPD) memory identification
52b95f7d00Smiod */
53b95f7d00Smiod
54b95f7d00Smiod #include <sys/param.h>
55b95f7d00Smiod #include <sys/systm.h>
56b95f7d00Smiod #include <sys/device.h>
57b95f7d00Smiod
58b95f7d00Smiod #include <dev/spdmemvar.h>
59b95f7d00Smiod
60b95f7d00Smiod /* Encodings of the size used/total byte for certain memory types */
61b95f7d00Smiod #define SPDMEM_SPDSIZE_MASK 0x0F /* SPD EEPROM Size */
62b95f7d00Smiod
63b95f7d00Smiod #define SPDMEM_SPDLEN_128 0x00 /* SPD EEPROM Sizes */
64b95f7d00Smiod #define SPDMEM_SPDLEN_176 0x10
65b95f7d00Smiod #define SPDMEM_SPDLEN_256 0x20
66b95f7d00Smiod #define SPDMEM_SPDLEN_MASK 0x70 /* Bits 4 - 6 */
67b95f7d00Smiod
68b9940182Sclaudio #define SPDMEM_DDR4_SPDLEN_128 0x01 /* SPD EEPROM Sizes */
69b9940182Sclaudio #define SPDMEM_DDR4_SPDLEN_256 0x02
70b9940182Sclaudio #define SPDMEM_DDR4_SPDLEN_384 0x03
71b9940182Sclaudio #define SPDMEM_DDR4_SPDLEN_512 0x04
72b9940182Sclaudio #define SPDMEM_DDR4_SPDLEN_MASK 0x0f /* Bits 4 - 6 */
73b9940182Sclaudio
74b95f7d00Smiod #define SPDMEM_SPDCRC_116 0x80 /* CRC Bytes covered */
75b95f7d00Smiod #define SPDMEM_SPDCRC_125 0x00
76b95f7d00Smiod #define SPDMEM_SPDCRC_MASK 0x80 /* Bit 7 */
77b95f7d00Smiod
78b95f7d00Smiod /* possible values for the memory type */
79b95f7d00Smiod #define SPDMEM_MEMTYPE_FPM 0x01
80b95f7d00Smiod #define SPDMEM_MEMTYPE_EDO 0x02
81b95f7d00Smiod #define SPDMEM_MEMTYPE_PIPE_NIBBLE 0x03
82b95f7d00Smiod #define SPDMEM_MEMTYPE_SDRAM 0x04
83b95f7d00Smiod #define SPDMEM_MEMTYPE_ROM 0x05
84b95f7d00Smiod #define SPDMEM_MEMTYPE_DDRSGRAM 0x06
85b95f7d00Smiod #define SPDMEM_MEMTYPE_DDRSDRAM 0x07
86b95f7d00Smiod #define SPDMEM_MEMTYPE_DDR2SDRAM 0x08
87b95f7d00Smiod #define SPDMEM_MEMTYPE_FBDIMM 0x09
88b95f7d00Smiod #define SPDMEM_MEMTYPE_FBDIMM_PROBE 0x0a
89b95f7d00Smiod #define SPDMEM_MEMTYPE_DDR3SDRAM 0x0b
90b9940182Sclaudio #define SPDMEM_MEMTYPE_DDR4SDRAM 0x0c
91b9940182Sclaudio /* 0xd reserved */
92b9940182Sclaudio #define SPDMEM_MEMTYPE_DDR4ESDRAM 0x0e
93b9940182Sclaudio #define SPDMEM_MEMTYPE_LPDDR3SDRAM 0x0f
94b9940182Sclaudio #define SPDMEM_MEMTYPE_LPDDR4SDRAM 0x10
95b9940182Sclaudio #define SPDMEM_MEMTYPE_LPDDR4XSDRAM 0x11
96b9940182Sclaudio #define SPDMEM_MEMTYPE_DDR5SDRAM 0x12
97b9940182Sclaudio #define SPDMEM_MEMTYPE_LPDDR5SDRAM 0x13
98b9940182Sclaudio
99b95f7d00Smiod #define SPDMEM_MEMTYPE_NONE 0xff
100b95f7d00Smiod
101b95f7d00Smiod #define SPDMEM_MEMTYPE_DIRECT_RAMBUS 0x01
102b95f7d00Smiod #define SPDMEM_MEMTYPE_RAMBUS 0x11
103b95f7d00Smiod
104b95f7d00Smiod /* possible values for the supply voltage */
105b95f7d00Smiod #define SPDMEM_VOLTAGE_TTL_5V 0x00
106b95f7d00Smiod #define SPDMEM_VOLTAGE_TTL_LV 0x01
107b95f7d00Smiod #define SPDMEM_VOLTAGE_HSTTL_1_5V 0x02
108b95f7d00Smiod #define SPDMEM_VOLTAGE_SSTL_3_3V 0x03
109b95f7d00Smiod #define SPDMEM_VOLTAGE_SSTL_2_5V 0x04
110b95f7d00Smiod #define SPDMEM_VOLTAGE_SSTL_1_8V 0x05
111b95f7d00Smiod
112b95f7d00Smiod /* possible values for module configuration */
113b95f7d00Smiod #define SPDMEM_MODCONFIG_PARITY 0x01
114b95f7d00Smiod #define SPDMEM_MODCONFIG_ECC 0x02
115b95f7d00Smiod
116b95f7d00Smiod /* for DDR2, module configuration is a bit-mask field */
117b95f7d00Smiod #define SPDMEM_MODCONFIG_HAS_DATA_PARITY 0x01
118b95f7d00Smiod #define SPDMEM_MODCONFIG_HAS_DATA_ECC 0x02
119b95f7d00Smiod #define SPDMEM_MODCONFIG_HAS_ADDR_CMD_PARITY 0x04
120b95f7d00Smiod
121b95f7d00Smiod /* possible values for the refresh field */
122b95f7d00Smiod #define SPDMEM_REFRESH_STD 0x00
123b95f7d00Smiod #define SPDMEM_REFRESH_QUARTER 0x01
124b95f7d00Smiod #define SPDMEM_REFRESH_HALF 0x02
125b95f7d00Smiod #define SPDMEM_REFRESH_TWOX 0x03
126b95f7d00Smiod #define SPDMEM_REFRESH_FOURX 0x04
127b95f7d00Smiod #define SPDMEM_REFRESH_EIGHTX 0x05
128b95f7d00Smiod #define SPDMEM_REFRESH_SELFREFRESH 0x80
129b95f7d00Smiod
130b95f7d00Smiod /* superset types */
131b95f7d00Smiod #define SPDMEM_SUPERSET_ESDRAM 0x01
132b95f7d00Smiod #define SPDMEM_SUPERSET_DDR_ESDRAM 0x02
133b95f7d00Smiod #define SPDMEM_SUPERSET_EDO_PEM 0x03
134b95f7d00Smiod #define SPDMEM_SUPERSET_SDR_PEM 0x04
135b95f7d00Smiod
136b95f7d00Smiod /* FPM and EDO DIMMS */
137b95f7d00Smiod #define SPDMEM_FPM_ROWS 0x00
138b95f7d00Smiod #define SPDMEM_FPM_COLS 0x01
139b95f7d00Smiod #define SPDMEM_FPM_BANKS 0x02
140b95f7d00Smiod #define SPDMEM_FPM_CONFIG 0x08
141b95f7d00Smiod #define SPDMEM_FPM_REFRESH 0x09
142b95f7d00Smiod #define SPDMEM_FPM_SUPERSET 0x0c
143b95f7d00Smiod
144b95f7d00Smiod /* PC66/PC100/PC133 SDRAM */
145b95f7d00Smiod #define SPDMEM_SDR_ROWS 0x00
146b95f7d00Smiod #define SPDMEM_SDR_COLS 0x01
147b95f7d00Smiod #define SPDMEM_SDR_BANKS 0x02
148b95f7d00Smiod #define SPDMEM_SDR_CYCLE 0x06
149b95f7d00Smiod #define SPDMEM_SDR_BANKS_PER_CHIP 0x0e
150b95f7d00Smiod #define SPDMEM_SDR_MOD_ATTRIB 0x12
151b95f7d00Smiod #define SPDMEM_SDR_SUPERSET 0x1d
152b95f7d00Smiod
153b95f7d00Smiod #define SPDMEM_SDR_FREQUENCY 126
154b95f7d00Smiod #define SPDMEM_SDR_CAS 127
155b95f7d00Smiod #define SPDMEM_SDR_FREQ_66 0x66
156b95f7d00Smiod #define SPDMEM_SDR_FREQ_100 0x64
157b95f7d00Smiod #define SPDMEM_SDR_FREQ_133 0x85
158b95f7d00Smiod #define SPDMEM_SDR_CAS2 (1 << 1)
159b95f7d00Smiod #define SPDMEM_SDR_CAS3 (1 << 2)
160b95f7d00Smiod
161b95f7d00Smiod /* Rambus Direct DRAM */
162b95f7d00Smiod #define SPDMEM_RDR_MODULE_TYPE 0x00
163b95f7d00Smiod #define SPDMEM_RDR_ROWS_COLS 0x01
164b95f7d00Smiod #define SPDMEM_RDR_BANK 0x02
165b95f7d00Smiod
166b95f7d00Smiod #define SPDMEM_RDR_TYPE_RIMM 1
167b95f7d00Smiod #define SPDMEM_RDR_TYPE_SORIMM 2
168b95f7d00Smiod #define SPDMEM_RDR_TYPE_EMBED 3
169b95f7d00Smiod #define SPDMEM_RDR_TYPE_RIMM32 4
170b95f7d00Smiod
171b95f7d00Smiod /* Dual Data Rate SDRAM */
172b95f7d00Smiod #define SPDMEM_DDR_ROWS 0x00
173b95f7d00Smiod #define SPDMEM_DDR_COLS 0x01
174b95f7d00Smiod #define SPDMEM_DDR_RANKS 0x02
175b95f7d00Smiod #define SPDMEM_DDR_DATAWIDTH 0x03
176b95f7d00Smiod #define SPDMEM_DDR_VOLTAGE 0x05
177b95f7d00Smiod #define SPDMEM_DDR_CYCLE 0x06
178b95f7d00Smiod #define SPDMEM_DDR_REFRESH 0x09
179b95f7d00Smiod #define SPDMEM_DDR_BANKS_PER_CHIP 0x0e
180b95f7d00Smiod #define SPDMEM_DDR_CAS 0x0f
181b95f7d00Smiod #define SPDMEM_DDR_MOD_ATTRIB 0x12
182b95f7d00Smiod #define SPDMEM_DDR_SUPERSET 0x1d
183b95f7d00Smiod
184b95f7d00Smiod #define SPDMEM_DDR_ATTRIB_REG (1 << 1)
185b95f7d00Smiod
186b95f7d00Smiod /* Dual Data Rate 2 SDRAM */
187b95f7d00Smiod #define SPDMEM_DDR2_ROWS 0x00
188b95f7d00Smiod #define SPDMEM_DDR2_COLS 0x01
189b95f7d00Smiod #define SPDMEM_DDR2_RANKS 0x02
190b95f7d00Smiod #define SPDMEM_DDR2_DATAWIDTH 0x03
191b95f7d00Smiod #define SPDMEM_DDR2_VOLTAGE 0x05
192b95f7d00Smiod #define SPDMEM_DDR2_CYCLE 0x06
193b95f7d00Smiod #define SPDMEM_DDR2_DIMMTYPE 0x11
194b95f7d00Smiod #define SPDMEM_DDR2_RANK_DENSITY 0x1c
195b95f7d00Smiod
196b95f7d00Smiod #define SPDMEM_DDR2_TYPE_REGMASK ((1 << 4) | (1 << 0))
197b95f7d00Smiod #define SPDMEM_DDR2_SODIMM (1 << 2)
198b95f7d00Smiod #define SPDMEM_DDR2_MICRO_DIMM (1 << 3)
199b95f7d00Smiod #define SPDMEM_DDR2_MINI_RDIMM (1 << 4)
200b95f7d00Smiod #define SPDMEM_DDR2_MINI_UDIMM (1 << 5)
201b95f7d00Smiod
202b95f7d00Smiod /* DDR2 FB-DIMM SDRAM */
203b95f7d00Smiod #define SPDMEM_FBDIMM_ADDR 0x01
204b95f7d00Smiod #define SPDMEM_FBDIMM_RANKS 0x04
205b95f7d00Smiod #define SPDMEM_FBDIMM_MTB_DIVIDEND 0x06
206b95f7d00Smiod #define SPDMEM_FBDIMM_MTB_DIVISOR 0x07
207b95f7d00Smiod #define SPDMEM_FBDIMM_PROTO 0x4e
208b95f7d00Smiod
209b95f7d00Smiod #define SPDMEM_FBDIMM_RANKS_WIDTH 0x07
210b95f7d00Smiod #define SPDMEM_FBDIMM_ADDR_BANKS 0x02
211b95f7d00Smiod #define SPDMEM_FBDIMM_ADDR_COL 0x0c
212b95f7d00Smiod #define SPDMEM_FBDIMM_ADDR_COL_SHIFT 2
213b95f7d00Smiod #define SPDMEM_FBDIMM_ADDR_ROW 0xe0
214b95f7d00Smiod #define SPDMEM_FBDIMM_ADDR_ROW_SHIFT 5
215b95f7d00Smiod #define SPDMEM_FBDIMM_PROTO_ECC (1 << 1)
216b95f7d00Smiod
217b95f7d00Smiod
218b95f7d00Smiod /* Dual Data Rate 3 SDRAM */
219b95f7d00Smiod #define SPDMEM_DDR3_MODTYPE 0x00
220b95f7d00Smiod #define SPDMEM_DDR3_DENSITY 0x01
221b95f7d00Smiod #define SPDMEM_DDR3_MOD_ORG 0x04
222b95f7d00Smiod #define SPDMEM_DDR3_DATAWIDTH 0x05
223b95f7d00Smiod #define SPDMEM_DDR3_MTB_DIVIDEND 0x07
224b95f7d00Smiod #define SPDMEM_DDR3_MTB_DIVISOR 0x08
225b95f7d00Smiod #define SPDMEM_DDR3_TCKMIN 0x09
226b95f7d00Smiod #define SPDMEM_DDR3_THERMAL 0x1d
227b95f7d00Smiod
228b95f7d00Smiod #define SPDMEM_DDR3_DENSITY_CAPMASK 0x0f
229b95f7d00Smiod #define SPDMEM_DDR3_MOD_ORG_CHIPWIDTH_MASK 0x07
230b95f7d00Smiod #define SPDMEM_DDR3_MOD_ORG_BANKS_SHIFT 3
231b95f7d00Smiod #define SPDMEM_DDR3_MOD_ORG_BANKS_MASK 0x07
232b95f7d00Smiod #define SPDMEM_DDR3_DATAWIDTH_ECCMASK (1 << 3)
233b95f7d00Smiod #define SPDMEM_DDR3_DATAWIDTH_PRIMASK 0x07
234b95f7d00Smiod #define SPDMEM_DDR3_THERMAL_PRESENT (1 << 7)
235b95f7d00Smiod
236b95f7d00Smiod #define SPDMEM_DDR3_RDIMM 0x01
237b95f7d00Smiod #define SPDMEM_DDR3_UDIMM 0x02
238b95f7d00Smiod #define SPDMEM_DDR3_SODIMM 0x03
239b95f7d00Smiod #define SPDMEM_DDR3_MICRO_DIMM 0x04
240b95f7d00Smiod #define SPDMEM_DDR3_MINI_RDIMM 0x05
241b95f7d00Smiod #define SPDMEM_DDR3_MINI_UDIMM 0x06
242b95f7d00Smiod
243b9940182Sclaudio /* Dual Data Rate 4 SDRAM */
244b9940182Sclaudio #define SPDMEM_DDR4_MODTYPE 0x00
245b9940182Sclaudio #define SPDMEM_DDR4_DENSITY 0x01
246b9940182Sclaudio #define SPDMEM_DDR4_PACK_TYPE 0x03
247b9940182Sclaudio #define SPDMEM_DDR4_MOD_ORG 0x09
248b9940182Sclaudio #define SPDMEM_DDR4_DATAWIDTH 0x0a
249b9940182Sclaudio #define SPDMEM_DDR4_THERMAL 0x0b
250b9940182Sclaudio #define SPDMEM_DDR4_TCKMIN_MTB 0x0f
251b9940182Sclaudio #define SPDMEM_DDR4_TCKMIN_FTB 0x7d /* not offset by 3 */
252b9940182Sclaudio
253b9940182Sclaudio #define SPDMEM_DDR4_DENSITY_CAPMASK 0x0f
254b9940182Sclaudio #define SPDMEM_DDR4_PACK_TYPE_SIG_LOAD_MASK 0x03
255b9940182Sclaudio #define SPDMEM_DDR4_PACK_TYPE_SIG_SINGLE_LOAD 0x02
256b9940182Sclaudio #define SPDMEM_DDR4_PACK_TYPE_DIE_COUNT_SHIFT 4
257b9940182Sclaudio #define SPDMEM_DDR4_PACK_TYPE_DIE_COUNT_MASK 0x07
258b9940182Sclaudio #define SPDMEM_DDR4_MOD_ORG_CHIPWIDTH_MASK 0x07
259b9940182Sclaudio #define SPDMEM_DDR4_MOD_ORG_BANKS_SHIFT 3
260b9940182Sclaudio #define SPDMEM_DDR4_MOD_ORG_BANKS_MASK 0x07
261b9940182Sclaudio #define SPDMEM_DDR4_DATAWIDTH_ECCMASK (1 << 3)
262b9940182Sclaudio #define SPDMEM_DDR4_DATAWIDTH_PRIMASK 0x07
263b9940182Sclaudio #define SPDMEM_DDR4_THERMAL_PRESENT (1 << 7)
264b9940182Sclaudio
265b9940182Sclaudio #define SPDMEM_DDR4_RDIMM 0x01
266b9940182Sclaudio #define SPDMEM_DDR4_UDIMM 0x02
267b9940182Sclaudio #define SPDMEM_DDR4_SODIMM 0x03
268b9940182Sclaudio #define SPDMEM_DDR4_LRDIMM 0x04
269b9940182Sclaudio #define SPDMEM_DDR4_MINI_RDIMM 0x05
270b9940182Sclaudio #define SPDMEM_DDR4_MINI_UDIMM 0x06
271b9940182Sclaudio #define SPDMEM_DDR4_LP_DIMM 0x07
272b9940182Sclaudio #define SPDMEM_DDR4_72B_SO_RDIMM 0x08
273b9940182Sclaudio #define SPDMEM_DDR4_72B_SO_UDIMM 0x09
274b9940182Sclaudio #define SPDMEM_DDR4_16B_SO_DIMM 0x0c
275b9940182Sclaudio #define SPDMEM_DDR4_32B_SO_DIMM 0x0d
276b9940182Sclaudio #define SPDMEM_DDR4_NON_DIMM 0x0e
277b9940182Sclaudio #define SPDMEM_DDR4_MODTYPE_MASK 0x0f
278b9940182Sclaudio #define SPDMEM_DDR4_MODTYPE_HYBRID 0x80
279b9940182Sclaudio
280b95f7d00Smiod static const uint8_t ddr2_cycle_tenths[] = {
281b95f7d00Smiod 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 25, 33, 66, 75, 0, 0
282b95f7d00Smiod };
283b95f7d00Smiod
284b95f7d00Smiod #define SPDMEM_TYPE_MAXLEN 16
285b95f7d00Smiod
286b95f7d00Smiod uint16_t spdmem_crc16(struct spdmem_softc *, int);
287b95f7d00Smiod static inline
288b95f7d00Smiod uint8_t spdmem_read(struct spdmem_softc *, uint8_t);
289b95f7d00Smiod void spdmem_sdram_decode(struct spdmem_softc *, struct spdmem *);
290b95f7d00Smiod void spdmem_rdr_decode(struct spdmem_softc *, struct spdmem *);
291b95f7d00Smiod void spdmem_ddr_decode(struct spdmem_softc *, struct spdmem *);
292b95f7d00Smiod void spdmem_ddr2_decode(struct spdmem_softc *, struct spdmem *);
293b95f7d00Smiod void spdmem_fbdimm_decode(struct spdmem_softc *, struct spdmem *);
294b95f7d00Smiod void spdmem_ddr3_decode(struct spdmem_softc *, struct spdmem *);
295b95f7d00Smiod
296b95f7d00Smiod struct cfdriver spdmem_cd = {
297b95f7d00Smiod NULL, "spdmem", DV_DULL
298b95f7d00Smiod };
299b95f7d00Smiod
300b95f7d00Smiod #define IS_RAMBUS_TYPE (s->sm_len < 4)
301b95f7d00Smiod
302b95f7d00Smiod static const char *spdmem_basic_types[] = {
303b95f7d00Smiod "unknown",
304b95f7d00Smiod "FPM",
305b95f7d00Smiod "EDO",
306b95f7d00Smiod "Pipelined Nibble",
307b95f7d00Smiod "SDRAM",
308b95f7d00Smiod "ROM",
309b95f7d00Smiod "DDR SGRAM",
310b95f7d00Smiod "DDR SDRAM",
311b95f7d00Smiod "DDR2 SDRAM",
312b95f7d00Smiod "DDR2 SDRAM FB-DIMM",
313b95f7d00Smiod "DDR2 SDRAM FB-DIMM Probe",
314b9940182Sclaudio "DDR3 SDRAM",
315b9940182Sclaudio "DDR4 SDRAM",
316b9940182Sclaudio "unknown",
317b9940182Sclaudio "DDR4E SDRAM",
318b9940182Sclaudio "LPDDR3 SDRAM",
319b9940182Sclaudio "LPDDR4 SDRAM",
320b9940182Sclaudio "LPDDR4X SDRAM",
321b9940182Sclaudio "DDR5 SDRAM",
322b9940182Sclaudio "LPDDR5 SDRAM"
323b95f7d00Smiod };
324b95f7d00Smiod
325b95f7d00Smiod static const char *spdmem_superset_types[] = {
326b95f7d00Smiod "unknown",
327b95f7d00Smiod "ESDRAM",
328b95f7d00Smiod "DDR ESDRAM",
329b95f7d00Smiod "PEM EDO",
330b95f7d00Smiod "PEM SDRAM"
331b95f7d00Smiod };
332b95f7d00Smiod
333b95f7d00Smiod static const char *spdmem_parity_types[] = {
334b95f7d00Smiod "non-parity",
335b95f7d00Smiod "data parity",
336b95f7d00Smiod "ECC",
337b95f7d00Smiod "data parity and ECC",
338b95f7d00Smiod "cmd/addr parity",
339b95f7d00Smiod "cmd/addr/data parity",
340b95f7d00Smiod "cmd/addr parity, data ECC",
341b95f7d00Smiod "cmd/addr/data parity, data ECC"
342b95f7d00Smiod };
343b95f7d00Smiod
344b95f7d00Smiod static inline uint8_t
spdmem_read(struct spdmem_softc * sc,uint8_t reg)345b95f7d00Smiod spdmem_read(struct spdmem_softc *sc, uint8_t reg)
346b95f7d00Smiod {
347b95f7d00Smiod return (*sc->sc_read)(sc, reg);
348b95f7d00Smiod }
349b95f7d00Smiod
350b95f7d00Smiod /* CRC functions used for certain memory types */
351b95f7d00Smiod uint16_t
spdmem_crc16(struct spdmem_softc * sc,int count)352b95f7d00Smiod spdmem_crc16(struct spdmem_softc *sc, int count)
353b95f7d00Smiod {
354b95f7d00Smiod uint16_t crc;
355b95f7d00Smiod int i, j;
356b95f7d00Smiod uint8_t val;
357b95f7d00Smiod crc = 0;
358b95f7d00Smiod for (j = 0; j <= count; j++) {
359b95f7d00Smiod val = spdmem_read(sc, j);
360b95f7d00Smiod crc = crc ^ val << 8;
361b95f7d00Smiod for (i = 0; i < 8; ++i)
362b95f7d00Smiod if (crc & 0x8000)
363b95f7d00Smiod crc = crc << 1 ^ 0x1021;
364b95f7d00Smiod else
365b95f7d00Smiod crc = crc << 1;
366b95f7d00Smiod }
367b95f7d00Smiod return (crc & 0xFFFF);
368b95f7d00Smiod }
369b95f7d00Smiod
370b95f7d00Smiod void
spdmem_sdram_decode(struct spdmem_softc * sc,struct spdmem * s)371b95f7d00Smiod spdmem_sdram_decode(struct spdmem_softc *sc, struct spdmem *s)
372b95f7d00Smiod {
373b95f7d00Smiod const char *type;
374b95f7d00Smiod int dimm_size, p_clk;
375b95f7d00Smiod int num_banks, per_chip;
376b95f7d00Smiod uint8_t rows, cols;
377b95f7d00Smiod
378b95f7d00Smiod type = spdmem_basic_types[s->sm_type];
379b95f7d00Smiod
380b95f7d00Smiod if (s->sm_data[SPDMEM_SDR_SUPERSET] == SPDMEM_SUPERSET_SDR_PEM)
381b95f7d00Smiod type = spdmem_superset_types[SPDMEM_SUPERSET_SDR_PEM];
382b95f7d00Smiod if (s->sm_data[SPDMEM_SDR_SUPERSET] == SPDMEM_SUPERSET_ESDRAM)
383b95f7d00Smiod type = spdmem_superset_types[SPDMEM_SUPERSET_ESDRAM];
384b95f7d00Smiod
385b95f7d00Smiod num_banks = s->sm_data[SPDMEM_SDR_BANKS];
386b95f7d00Smiod per_chip = s->sm_data[SPDMEM_SDR_BANKS_PER_CHIP];
387b95f7d00Smiod rows = s->sm_data[SPDMEM_SDR_ROWS] & 0x0f;
388b95f7d00Smiod cols = s->sm_data[SPDMEM_SDR_COLS] & 0x0f;
389b95f7d00Smiod dimm_size = (1 << (rows + cols - 17)) * num_banks * per_chip;
390b95f7d00Smiod
391b95f7d00Smiod if (dimm_size > 0) {
392b95f7d00Smiod if (dimm_size < 1024)
393b95f7d00Smiod printf(" %dMB", dimm_size);
394b95f7d00Smiod else
395b95f7d00Smiod printf(" %dGB", dimm_size / 1024);
396b95f7d00Smiod }
397b95f7d00Smiod
398b95f7d00Smiod printf(" %s", type);
399b95f7d00Smiod
400b95f7d00Smiod if (s->sm_data[SPDMEM_DDR_MOD_ATTRIB] & SPDMEM_DDR_ATTRIB_REG)
401b95f7d00Smiod printf(" registered");
402b95f7d00Smiod
403b95f7d00Smiod if (s->sm_data[SPDMEM_FPM_CONFIG] < 8)
404b95f7d00Smiod printf(" %s",
405b95f7d00Smiod spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]);
406b95f7d00Smiod
407b95f7d00Smiod p_clk = 66;
408b95f7d00Smiod if (s->sm_len >= 128) {
409b95f7d00Smiod switch (spdmem_read(sc, SPDMEM_SDR_FREQUENCY)) {
410b95f7d00Smiod case SPDMEM_SDR_FREQ_100:
411b95f7d00Smiod case SPDMEM_SDR_FREQ_133:
412b95f7d00Smiod /* We need to check ns to decide here */
413b95f7d00Smiod if (s->sm_data[SPDMEM_SDR_CYCLE] < 0x80)
414b95f7d00Smiod p_clk = 133;
415b95f7d00Smiod else
416b95f7d00Smiod p_clk = 100;
417b95f7d00Smiod break;
418b95f7d00Smiod case SPDMEM_SDR_FREQ_66:
419b95f7d00Smiod default:
420b95f7d00Smiod p_clk = 66;
421b95f7d00Smiod break;
422b95f7d00Smiod }
423b95f7d00Smiod }
424b95f7d00Smiod printf(" PC%d", p_clk);
425b95f7d00Smiod
426b95f7d00Smiod /* Print CAS latency */
427b95f7d00Smiod if (s->sm_len < 128)
428b95f7d00Smiod return;
429b95f7d00Smiod if (spdmem_read(sc, SPDMEM_SDR_CAS) & SPDMEM_SDR_CAS2)
430b95f7d00Smiod printf("CL2");
431b95f7d00Smiod else if (spdmem_read(sc, SPDMEM_SDR_CAS) & SPDMEM_SDR_CAS3)
432b95f7d00Smiod printf("CL3");
433b95f7d00Smiod }
434b95f7d00Smiod
435b95f7d00Smiod void
spdmem_rdr_decode(struct spdmem_softc * sc,struct spdmem * s)436b95f7d00Smiod spdmem_rdr_decode(struct spdmem_softc *sc, struct spdmem *s)
437b95f7d00Smiod {
438b95f7d00Smiod int rimm_size;
439b95f7d00Smiod uint8_t row_bits, col_bits, bank_bits;
440b95f7d00Smiod
441b95f7d00Smiod row_bits = s->sm_data[SPDMEM_RDR_ROWS_COLS] >> 4;
442b95f7d00Smiod col_bits = s->sm_data[SPDMEM_RDR_ROWS_COLS] & 0x0f;
443b95f7d00Smiod bank_bits = s->sm_data[SPDMEM_RDR_BANK] & 0x07;
444b95f7d00Smiod
445b95f7d00Smiod /* subtracting 13 here is a cheaper way of dividing by 8k later */
446b95f7d00Smiod rimm_size = 1 << (row_bits + col_bits + bank_bits - 13);
447b95f7d00Smiod
448b95f7d00Smiod if (rimm_size < 1024)
449b95f7d00Smiod printf(" %dMB ", rimm_size);
450b95f7d00Smiod else
451b95f7d00Smiod printf(" %dGB ", rimm_size / 1024);
452b95f7d00Smiod
453b95f7d00Smiod switch(s->sm_data[SPDMEM_RDR_MODULE_TYPE]) {
454b95f7d00Smiod case SPDMEM_RDR_TYPE_RIMM:
455b95f7d00Smiod printf("RIMM");
456b95f7d00Smiod break;
457b95f7d00Smiod case SPDMEM_RDR_TYPE_SORIMM:
458b95f7d00Smiod printf("SO-RIMM");
459b95f7d00Smiod break;
460b95f7d00Smiod case SPDMEM_RDR_TYPE_EMBED:
461b95f7d00Smiod printf("Embedded Rambus");
462b95f7d00Smiod break;
463b95f7d00Smiod case SPDMEM_RDR_TYPE_RIMM32:
464b95f7d00Smiod printf("RIMM32");
465b95f7d00Smiod break;
466b95f7d00Smiod }
467b95f7d00Smiod }
468b95f7d00Smiod
469b95f7d00Smiod void
spdmem_ddr_decode(struct spdmem_softc * sc,struct spdmem * s)470b95f7d00Smiod spdmem_ddr_decode(struct spdmem_softc *sc, struct spdmem *s)
471b95f7d00Smiod {
472b95f7d00Smiod const char *type;
473b95f7d00Smiod int dimm_size, cycle_time, d_clk, p_clk, bits;
474b95f7d00Smiod int i, num_banks, per_chip;
475b95f7d00Smiod uint8_t config, rows, cols, cl;
476b95f7d00Smiod
477b95f7d00Smiod type = spdmem_basic_types[s->sm_type];
478b95f7d00Smiod
479b95f7d00Smiod if (s->sm_data[SPDMEM_DDR_SUPERSET] == SPDMEM_SUPERSET_DDR_ESDRAM)
480b95f7d00Smiod type = spdmem_superset_types[SPDMEM_SUPERSET_DDR_ESDRAM];
481b95f7d00Smiod
482b95f7d00Smiod num_banks = s->sm_data[SPDMEM_SDR_BANKS];
483b95f7d00Smiod per_chip = s->sm_data[SPDMEM_SDR_BANKS_PER_CHIP];
484b95f7d00Smiod rows = s->sm_data[SPDMEM_SDR_ROWS] & 0x0f;
485b95f7d00Smiod cols = s->sm_data[SPDMEM_SDR_COLS] & 0x0f;
486b95f7d00Smiod dimm_size = (1 << (rows + cols - 17)) * num_banks * per_chip;
487b95f7d00Smiod
488b95f7d00Smiod if (dimm_size > 0) {
489b95f7d00Smiod if (dimm_size < 1024)
490b95f7d00Smiod printf(" %dMB", dimm_size);
491b95f7d00Smiod else
492b95f7d00Smiod printf(" %dGB", dimm_size / 1024);
493b95f7d00Smiod }
494b95f7d00Smiod
495b95f7d00Smiod printf(" %s", type);
496b95f7d00Smiod
497b95f7d00Smiod if (s->sm_data[SPDMEM_DDR_MOD_ATTRIB] & SPDMEM_DDR_ATTRIB_REG)
498b95f7d00Smiod printf(" registered");
499b95f7d00Smiod
500b95f7d00Smiod if (s->sm_data[SPDMEM_FPM_CONFIG] < 8)
501b95f7d00Smiod printf(" %s",
502b95f7d00Smiod spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]);
503b95f7d00Smiod
504b95f7d00Smiod /* cycle_time is expressed in units of 0.01 ns */
505b95f7d00Smiod cycle_time = (s->sm_data[SPDMEM_DDR_CYCLE] >> 4) * 100 +
506b95f7d00Smiod (s->sm_data[SPDMEM_DDR_CYCLE] & 0x0f) * 10;
507b95f7d00Smiod
508b95f7d00Smiod if (cycle_time != 0) {
509b95f7d00Smiod /*
510b95f7d00Smiod * cycle time is scaled by a factor of 100 to avoid using
511b95f7d00Smiod * floating point. Calculate memory speed as the number
512b95f7d00Smiod * of cycles per microsecond.
513b95f7d00Smiod * DDR uses dual-pumped clock
514b95f7d00Smiod */
515b95f7d00Smiod d_clk = 100 * 1000 * 2;
516b95f7d00Smiod config = s->sm_data[SPDMEM_FPM_CONFIG];
517b95f7d00Smiod bits = s->sm_data[SPDMEM_DDR_DATAWIDTH] |
518b95f7d00Smiod (s->sm_data[SPDMEM_DDR_DATAWIDTH + 1] << 8);
519b95f7d00Smiod if (config == 1 || config == 2)
520b95f7d00Smiod bits -= 8;
521b95f7d00Smiod
522b95f7d00Smiod d_clk /= cycle_time;
523b95f7d00Smiod p_clk = d_clk * bits / 8;
524b95f7d00Smiod if ((p_clk % 100) >= 50)
525b95f7d00Smiod p_clk += 50;
526b95f7d00Smiod p_clk -= p_clk % 100;
527b95f7d00Smiod printf(" PC%d", p_clk);
528b95f7d00Smiod }
529b95f7d00Smiod
530b95f7d00Smiod /* Print CAS latency */
531b95f7d00Smiod for (i = 6; i >= 0; i--) {
532b95f7d00Smiod if (s->sm_data[SPDMEM_DDR_CAS] & (1 << i)) {
533b95f7d00Smiod cl = ((i * 10) / 2) + 10;
534b95f7d00Smiod printf("CL%d.%d", cl / 10, cl % 10);
535b95f7d00Smiod break;
536b95f7d00Smiod }
537b95f7d00Smiod }
538b95f7d00Smiod }
539b95f7d00Smiod
540b95f7d00Smiod void
spdmem_ddr2_decode(struct spdmem_softc * sc,struct spdmem * s)541b95f7d00Smiod spdmem_ddr2_decode(struct spdmem_softc *sc, struct spdmem *s)
542b95f7d00Smiod {
543b95f7d00Smiod const char *type;
544b95f7d00Smiod int dimm_size, cycle_time, d_clk, p_clk, bits;
545b95f7d00Smiod int i, num_ranks, density;
546b95f7d00Smiod uint8_t config;
547b95f7d00Smiod
548b95f7d00Smiod type = spdmem_basic_types[s->sm_type];
549b95f7d00Smiod
550b95f7d00Smiod num_ranks = (s->sm_data[SPDMEM_DDR2_RANKS] & 0x7) + 1;
551b95f7d00Smiod density = (s->sm_data[SPDMEM_DDR2_RANK_DENSITY] & 0xf0) |
552b95f7d00Smiod ((s->sm_data[SPDMEM_DDR2_RANK_DENSITY] & 0x0f) << 8);
553b95f7d00Smiod dimm_size = num_ranks * density * 4;
554b95f7d00Smiod
555b95f7d00Smiod if (dimm_size > 0) {
556b95f7d00Smiod if (dimm_size < 1024)
557b95f7d00Smiod printf(" %dMB", dimm_size);
558b95f7d00Smiod else
559b95f7d00Smiod printf(" %dGB", dimm_size / 1024);
560b95f7d00Smiod }
561b95f7d00Smiod
562b95f7d00Smiod printf(" %s", type);
563b95f7d00Smiod
564b95f7d00Smiod if (s->sm_data[SPDMEM_DDR2_DIMMTYPE] & SPDMEM_DDR2_TYPE_REGMASK)
565b95f7d00Smiod printf(" registered");
566b95f7d00Smiod
567b95f7d00Smiod if (s->sm_data[SPDMEM_FPM_CONFIG] < 8)
568b95f7d00Smiod printf(" %s",
569b95f7d00Smiod spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]);
570b95f7d00Smiod
571b95f7d00Smiod /* cycle_time is expressed in units of 0.01 ns */
572b95f7d00Smiod cycle_time = (s->sm_data[SPDMEM_DDR2_CYCLE] >> 4) * 100 +
573b95f7d00Smiod ddr2_cycle_tenths[(s->sm_data[SPDMEM_DDR2_CYCLE] & 0x0f)];
574b95f7d00Smiod
575b95f7d00Smiod if (cycle_time != 0) {
576b95f7d00Smiod /*
577b95f7d00Smiod * cycle time is scaled by a factor of 100 to avoid using
578b95f7d00Smiod * floating point. Calculate memory speed as the number
579b95f7d00Smiod * of cycles per microsecond.
580b95f7d00Smiod * DDR2 uses quad-pumped clock
581b95f7d00Smiod */
582b95f7d00Smiod d_clk = 100 * 1000 * 4;
583b95f7d00Smiod config = s->sm_data[SPDMEM_FPM_CONFIG];
584b95f7d00Smiod bits = s->sm_data[SPDMEM_DDR2_DATAWIDTH];
585b95f7d00Smiod if ((config & 0x03) != 0)
586b95f7d00Smiod bits -= 8;
587b95f7d00Smiod d_clk /= cycle_time;
588b95f7d00Smiod d_clk = (d_clk + 1) / 2;
589b95f7d00Smiod p_clk = d_clk * bits / 8;
590b95f7d00Smiod p_clk -= p_clk % 100;
591b95f7d00Smiod printf(" PC2-%d", p_clk);
592b95f7d00Smiod }
593b95f7d00Smiod
594b95f7d00Smiod /* Print CAS latency */
595182c86a5Sjsg for (i = 7; i >= 2; i--) {
596182c86a5Sjsg if (s->sm_data[SPDMEM_DDR_CAS] & (1 << i)) {
597b95f7d00Smiod printf("CL%d", i);
598b95f7d00Smiod break;
599b95f7d00Smiod }
600b95f7d00Smiod }
601b95f7d00Smiod
602b95f7d00Smiod switch (s->sm_data[SPDMEM_DDR2_DIMMTYPE]) {
603b95f7d00Smiod case SPDMEM_DDR2_SODIMM:
604b95f7d00Smiod printf(" SO-DIMM");
605b95f7d00Smiod break;
606b95f7d00Smiod case SPDMEM_DDR2_MICRO_DIMM:
607b95f7d00Smiod printf(" Micro-DIMM");
608b95f7d00Smiod break;
609b95f7d00Smiod case SPDMEM_DDR2_MINI_RDIMM:
610b95f7d00Smiod case SPDMEM_DDR2_MINI_UDIMM:
611b95f7d00Smiod printf(" Mini-DIMM");
612b95f7d00Smiod break;
613b95f7d00Smiod }
614b95f7d00Smiod }
615b95f7d00Smiod
616b95f7d00Smiod void
spdmem_fbdimm_decode(struct spdmem_softc * sc,struct spdmem * s)617b95f7d00Smiod spdmem_fbdimm_decode(struct spdmem_softc *sc, struct spdmem *s)
618b95f7d00Smiod {
619298c3045Schl int dimm_size, cycle_time, d_clk, p_clk, bits;
620298c3045Schl uint8_t rows, cols, dividend, divisor;
621b95f7d00Smiod /*
622b95f7d00Smiod * FB-DIMM is very much like DDR3
623b95f7d00Smiod */
624b95f7d00Smiod
625b95f7d00Smiod cols = (s->sm_data[SPDMEM_FBDIMM_ADDR] & SPDMEM_FBDIMM_ADDR_COL) >>
626b95f7d00Smiod SPDMEM_FBDIMM_ADDR_COL_SHIFT;
627b95f7d00Smiod rows = (s->sm_data[SPDMEM_FBDIMM_ADDR] & SPDMEM_FBDIMM_ADDR_ROW) >>
628b95f7d00Smiod SPDMEM_FBDIMM_ADDR_ROW_SHIFT;
629b95f7d00Smiod dimm_size = rows + 12 + cols + 9 - 20 - 3;
630b95f7d00Smiod
631b95f7d00Smiod if (dimm_size < 1024)
632b95f7d00Smiod printf(" %dMB", dimm_size);
633b95f7d00Smiod else
634b95f7d00Smiod printf(" %dGB", dimm_size / 1024);
635b95f7d00Smiod
636b95f7d00Smiod dividend = s->sm_data[SPDMEM_FBDIMM_MTB_DIVIDEND];
637b95f7d00Smiod divisor = s->sm_data[SPDMEM_FBDIMM_MTB_DIVISOR];
638b95f7d00Smiod
639b95f7d00Smiod cycle_time = (1000 * dividend + (divisor / 2)) / divisor;
640b95f7d00Smiod
641b95f7d00Smiod if (cycle_time != 0) {
642b95f7d00Smiod /*
643b95f7d00Smiod * cycle time is scaled by a factor of 1000 to avoid using
644b95f7d00Smiod * floating point. Calculate memory speed as the number
645b95f7d00Smiod * of cycles per microsecond.
646b95f7d00Smiod */
647b95f7d00Smiod d_clk = 1000 * 1000;
648b95f7d00Smiod
649b95f7d00Smiod /* DDR2 FB-DIMM uses a dual-pumped clock */
650b95f7d00Smiod d_clk *= 2;
651b95f7d00Smiod bits = 1 << ((s->sm_data[SPDMEM_FBDIMM_RANKS] &
652b95f7d00Smiod SPDMEM_FBDIMM_RANKS_WIDTH) + 2);
653b95f7d00Smiod
654b95f7d00Smiod p_clk = (d_clk * bits) / 8 / cycle_time;
655b95f7d00Smiod p_clk -= p_clk % 100;
656b95f7d00Smiod printf(" PC2-%d", p_clk);
657b95f7d00Smiod }
658b95f7d00Smiod }
659b95f7d00Smiod
660b95f7d00Smiod void
spdmem_ddr3_decode(struct spdmem_softc * sc,struct spdmem * s)661b95f7d00Smiod spdmem_ddr3_decode(struct spdmem_softc *sc, struct spdmem *s)
662b95f7d00Smiod {
663b95f7d00Smiod const char *type;
664b95f7d00Smiod int dimm_size, cycle_time, d_clk, p_clk, bits;
665b95f7d00Smiod uint8_t mtype, chipsize, dividend, divisor;
666b95f7d00Smiod uint8_t datawidth, chipwidth, physbanks;
667b95f7d00Smiod
668b95f7d00Smiod type = spdmem_basic_types[s->sm_type];
669b95f7d00Smiod
670b95f7d00Smiod chipsize = s->sm_data[SPDMEM_DDR3_DENSITY] &
671b95f7d00Smiod SPDMEM_DDR3_DENSITY_CAPMASK;
672b95f7d00Smiod datawidth = s->sm_data[SPDMEM_DDR3_DATAWIDTH] &
673b95f7d00Smiod SPDMEM_DDR3_DATAWIDTH_PRIMASK;
674b95f7d00Smiod chipwidth = s->sm_data[SPDMEM_DDR3_MOD_ORG] &
675b95f7d00Smiod SPDMEM_DDR3_MOD_ORG_CHIPWIDTH_MASK;
676b95f7d00Smiod physbanks = (s->sm_data[SPDMEM_DDR3_MOD_ORG] >>
677b95f7d00Smiod SPDMEM_DDR3_MOD_ORG_BANKS_SHIFT) & SPDMEM_DDR3_MOD_ORG_BANKS_MASK;
678b95f7d00Smiod
679b95f7d00Smiod dimm_size = (chipsize + 28 - 20) - 3 + (datawidth + 3) -
680b95f7d00Smiod (chipwidth + 2);
681b95f7d00Smiod dimm_size = (1 << dimm_size) * (physbanks + 1);
682b95f7d00Smiod
683b95f7d00Smiod if (dimm_size < 1024)
684b95f7d00Smiod printf(" %dMB", dimm_size);
685b95f7d00Smiod else
686b95f7d00Smiod printf(" %dGB", dimm_size / 1024);
687b95f7d00Smiod
688b95f7d00Smiod printf(" %s", type);
689b95f7d00Smiod
690b95f7d00Smiod mtype = s->sm_data[SPDMEM_DDR3_MODTYPE];
691b95f7d00Smiod if (mtype == SPDMEM_DDR3_RDIMM || mtype == SPDMEM_DDR3_MINI_RDIMM)
692b95f7d00Smiod printf(" registered");
693b95f7d00Smiod
694b95f7d00Smiod if (s->sm_data[SPDMEM_DDR3_DATAWIDTH] & SPDMEM_DDR3_DATAWIDTH_ECCMASK)
695b95f7d00Smiod printf(" ECC");
696b95f7d00Smiod
697b95f7d00Smiod dividend = s->sm_data[SPDMEM_DDR3_MTB_DIVIDEND];
698b95f7d00Smiod divisor = s->sm_data[SPDMEM_DDR3_MTB_DIVISOR];
699b95f7d00Smiod cycle_time = (1000 * dividend + (divisor / 2)) / divisor;
700b95f7d00Smiod cycle_time *= s->sm_data[SPDMEM_DDR3_TCKMIN];
701b95f7d00Smiod
702b95f7d00Smiod if (cycle_time != 0) {
703b95f7d00Smiod /*
704b95f7d00Smiod * cycle time is scaled by a factor of 1000 to avoid using
705b95f7d00Smiod * floating point. Calculate memory speed as the number
706b95f7d00Smiod * of cycles per microsecond.
707b95f7d00Smiod * DDR3 uses a dual-pumped clock
708b95f7d00Smiod */
709b95f7d00Smiod d_clk = 1000 * 1000;
710b95f7d00Smiod d_clk *= 2;
711b95f7d00Smiod bits = 1 << ((s->sm_data[SPDMEM_DDR3_DATAWIDTH] &
712b95f7d00Smiod SPDMEM_DDR3_DATAWIDTH_PRIMASK) + 3);
713b95f7d00Smiod /*
714b95f7d00Smiod * Calculate p_clk first, since for DDR3 we need maximum
715b95f7d00Smiod * significance. DDR3 rating is not rounded to a multiple
716b95f7d00Smiod * of 100. This results in cycle_time of 1.5ns displayed
717b95f7d00Smiod * as p_clk PC3-10666 (d_clk DDR3-1333)
718b95f7d00Smiod */
719b95f7d00Smiod p_clk = (d_clk * bits) / 8 / cycle_time;
720b95f7d00Smiod p_clk -= (p_clk % 100);
721b95f7d00Smiod d_clk = ((d_clk + cycle_time / 2) ) / cycle_time;
722b95f7d00Smiod printf(" PC3-%d", p_clk);
723b95f7d00Smiod }
724b95f7d00Smiod
725b95f7d00Smiod switch (s->sm_data[SPDMEM_DDR3_MODTYPE]) {
726b95f7d00Smiod case SPDMEM_DDR3_SODIMM:
727b95f7d00Smiod printf(" SO-DIMM");
728b95f7d00Smiod break;
729b95f7d00Smiod case SPDMEM_DDR3_MICRO_DIMM:
730b95f7d00Smiod printf(" Micro-DIMM");
731b95f7d00Smiod break;
732b95f7d00Smiod case SPDMEM_DDR3_MINI_RDIMM:
733b95f7d00Smiod case SPDMEM_DDR3_MINI_UDIMM:
734b95f7d00Smiod printf(" Mini-DIMM");
735b95f7d00Smiod break;
736b95f7d00Smiod }
737b95f7d00Smiod
738b95f7d00Smiod if (s->sm_data[SPDMEM_DDR3_THERMAL] & SPDMEM_DDR3_THERMAL_PRESENT)
739b95f7d00Smiod printf(" with thermal sensor");
740b95f7d00Smiod }
741b95f7d00Smiod
742b9940182Sclaudio void
spdmem_ddr4_decode(struct spdmem_softc * sc,struct spdmem * s)743b9940182Sclaudio spdmem_ddr4_decode(struct spdmem_softc *sc, struct spdmem *s)
744b9940182Sclaudio {
745b9940182Sclaudio static const int ddr4_chipsize[16] = { 256, 512, 1024, 2048, 4096,
746b9940182Sclaudio 8 * 1024, 16 * 1024, 32 * 1024, 12 * 1024, 24 * 1024,
747b9940182Sclaudio 3 * 1024, 6 * 1024, 18 * 1024 };
748b9940182Sclaudio const char *type;
749b9940182Sclaudio int dimm_size, cycle_time, d_clk, p_clk, bits;
750b9940182Sclaudio uint8_t mtype, chipsize, mtb;
751b9940182Sclaudio int8_t ftb;
752b9940182Sclaudio uint8_t datawidth, chipwidth, physbanks, diecount = 0;
753b9940182Sclaudio
754b9940182Sclaudio type = spdmem_basic_types[s->sm_type];
755b9940182Sclaudio
756b9940182Sclaudio chipsize = s->sm_data[SPDMEM_DDR4_DENSITY] &
757b9940182Sclaudio SPDMEM_DDR4_DENSITY_CAPMASK;
758b9940182Sclaudio datawidth = s->sm_data[SPDMEM_DDR4_DATAWIDTH] &
759b9940182Sclaudio SPDMEM_DDR4_DATAWIDTH_PRIMASK;
760b9940182Sclaudio chipwidth = s->sm_data[SPDMEM_DDR4_MOD_ORG] &
761b9940182Sclaudio SPDMEM_DDR4_MOD_ORG_CHIPWIDTH_MASK;
762b9940182Sclaudio physbanks = (s->sm_data[SPDMEM_DDR4_MOD_ORG] >>
763b9940182Sclaudio SPDMEM_DDR4_MOD_ORG_BANKS_SHIFT) & SPDMEM_DDR4_MOD_ORG_BANKS_MASK;
764b9940182Sclaudio
765b9940182Sclaudio if ((s->sm_data[SPDMEM_DDR4_PACK_TYPE] &
766b9940182Sclaudio SPDMEM_DDR4_PACK_TYPE_SIG_LOAD_MASK) ==
767b9940182Sclaudio SPDMEM_DDR4_PACK_TYPE_SIG_SINGLE_LOAD) {
768b9940182Sclaudio diecount = (s->sm_data[SPDMEM_DDR4_PACK_TYPE] >>
769b9940182Sclaudio SPDMEM_DDR4_PACK_TYPE_DIE_COUNT_SHIFT) &
770b9940182Sclaudio SPDMEM_DDR4_PACK_TYPE_DIE_COUNT_MASK;
771b9940182Sclaudio }
772b9940182Sclaudio
773*b7d4deb4Skettenis dimm_size = (datawidth + 3) - (chipwidth + 2);
774*b7d4deb4Skettenis dimm_size = (ddr4_chipsize[chipsize] / 8) * (1 << dimm_size) *
775b9940182Sclaudio (physbanks + 1) * (diecount + 1);
776b9940182Sclaudio
777b9940182Sclaudio if (dimm_size < 1024)
778b9940182Sclaudio printf(" %dMB", dimm_size);
779b9940182Sclaudio else
780b9940182Sclaudio printf(" %dGB", dimm_size / 1024);
781b9940182Sclaudio
782b9940182Sclaudio printf(" %s", type);
783b9940182Sclaudio
784b9940182Sclaudio mtype = s->sm_data[SPDMEM_DDR4_MODTYPE];
785b9940182Sclaudio if (mtype & SPDMEM_DDR4_MODTYPE_HYBRID)
786b9940182Sclaudio printf(" hybrid");
787b9940182Sclaudio mtype &= SPDMEM_DDR4_MODTYPE_MASK;
788b9940182Sclaudio if (mtype == SPDMEM_DDR4_RDIMM || mtype == SPDMEM_DDR4_MINI_RDIMM ||
789b9940182Sclaudio mtype == SPDMEM_DDR4_72B_SO_RDIMM)
790b9940182Sclaudio printf(" registered");
791b9940182Sclaudio if (mtype == SPDMEM_DDR4_72B_SO_UDIMM ||
792b9940182Sclaudio mtype == SPDMEM_DDR4_72B_SO_RDIMM)
793b9940182Sclaudio printf(" 72-bit");
794b9940182Sclaudio if (mtype == SPDMEM_DDR4_32B_SO_DIMM)
795b9940182Sclaudio printf(" 32-bit");
796b9940182Sclaudio if (mtype == SPDMEM_DDR4_16B_SO_DIMM)
797b9940182Sclaudio printf(" 16-bit");
798b9940182Sclaudio
799b9940182Sclaudio if (s->sm_data[SPDMEM_DDR4_DATAWIDTH] & SPDMEM_DDR4_DATAWIDTH_ECCMASK)
800b9940182Sclaudio printf(" ECC");
801b9940182Sclaudio
802b9940182Sclaudio mtb = s->sm_data[SPDMEM_DDR4_TCKMIN_MTB];
803b9940182Sclaudio /* SPDMEM_DDR4_TCKMIN_FTB (addr 125) is outside of s->sm_data */
804b9940182Sclaudio ftb = spdmem_read(sc, SPDMEM_DDR4_TCKMIN_FTB);
805b9940182Sclaudio cycle_time = mtb * 125 + ftb; /* in ps */
806b9940182Sclaudio
807b9940182Sclaudio if (cycle_time != 0) {
808b9940182Sclaudio /*
809b9940182Sclaudio * cycle time is scaled by a factor of 1000 to avoid using
810b9940182Sclaudio * floating point. Calculate memory speed as the number
811b9940182Sclaudio * of cycles per microsecond.
812b9940182Sclaudio * DDR4 uses a dual-pumped clock
813b9940182Sclaudio */
814b9940182Sclaudio d_clk = 1000 * 1000;
815b9940182Sclaudio d_clk *= 2;
816b9940182Sclaudio bits = 1 << ((s->sm_data[SPDMEM_DDR4_DATAWIDTH] &
817b9940182Sclaudio SPDMEM_DDR4_DATAWIDTH_PRIMASK) + 3);
818b9940182Sclaudio
819b9940182Sclaudio p_clk = (d_clk * bits) / 8 / cycle_time;
820b9940182Sclaudio p_clk -= (p_clk % 100);
821b9940182Sclaudio printf(" PC4-%d", p_clk);
822b9940182Sclaudio }
823b9940182Sclaudio
824b9940182Sclaudio switch (s->sm_data[SPDMEM_DDR4_MODTYPE] & SPDMEM_DDR4_MODTYPE_MASK) {
825b9940182Sclaudio case SPDMEM_DDR4_SODIMM:
826b9940182Sclaudio case SPDMEM_DDR4_72B_SO_RDIMM:
827b9940182Sclaudio case SPDMEM_DDR4_72B_SO_UDIMM:
828b9940182Sclaudio case SPDMEM_DDR4_16B_SO_DIMM:
829b9940182Sclaudio case SPDMEM_DDR4_32B_SO_DIMM:
830b9940182Sclaudio printf(" SO-DIMM");
831b9940182Sclaudio break;
832b9940182Sclaudio case SPDMEM_DDR4_LRDIMM:
833b9940182Sclaudio printf(" LR-DIMM");
834b9940182Sclaudio break;
835b9940182Sclaudio case SPDMEM_DDR4_MINI_RDIMM:
836b9940182Sclaudio case SPDMEM_DDR4_MINI_UDIMM:
837b9940182Sclaudio printf(" Mini-DIMM");
838b9940182Sclaudio break;
839b9940182Sclaudio case SPDMEM_DDR4_LP_DIMM:
840b9940182Sclaudio printf(" LP-DIMM");
841b9940182Sclaudio break;
842b9940182Sclaudio case SPDMEM_DDR4_NON_DIMM:
843b9940182Sclaudio printf(" non-DIMM solution");
844b9940182Sclaudio break;
845b9940182Sclaudio }
846b9940182Sclaudio
847b9940182Sclaudio if (s->sm_data[SPDMEM_DDR4_THERMAL] & SPDMEM_DDR4_THERMAL_PRESENT)
848b9940182Sclaudio printf(" with thermal sensor");
849b9940182Sclaudio }
850b9940182Sclaudio
851b95f7d00Smiod int
spdmem_probe(struct spdmem_softc * sc)852b95f7d00Smiod spdmem_probe(struct spdmem_softc *sc)
853b95f7d00Smiod {
854b95f7d00Smiod uint8_t i, val, type;
855b95f7d00Smiod int cksum = 0;
856b95f7d00Smiod int spd_len, spd_crc_cover;
857b95f7d00Smiod uint16_t crc_calc, crc_spd;
858b95f7d00Smiod
859b95f7d00Smiod type = spdmem_read(sc, 2);
860b95f7d00Smiod /* For older memory types, validate the checksum over 1st 63 bytes */
861b95f7d00Smiod if (type <= SPDMEM_MEMTYPE_DDR2SDRAM) {
862b95f7d00Smiod for (i = 0; i < 63; i++)
863b95f7d00Smiod cksum += spdmem_read(sc, i);
864b95f7d00Smiod
865b95f7d00Smiod val = spdmem_read(sc, 63);
866b95f7d00Smiod
867b95f7d00Smiod if (cksum == 0 || (cksum & 0xff) != val) {
868b95f7d00Smiod return 0;
869b95f7d00Smiod } else
870b95f7d00Smiod return 1;
871b95f7d00Smiod }
872b95f7d00Smiod
873b95f7d00Smiod /* For DDR3 and FBDIMM, verify the CRC */
874b95f7d00Smiod else if (type <= SPDMEM_MEMTYPE_DDR3SDRAM) {
875b95f7d00Smiod spd_len = spdmem_read(sc, 0);
876c16bd265Sjsg if (spd_len & SPDMEM_SPDCRC_116)
877b95f7d00Smiod spd_crc_cover = 116;
878b95f7d00Smiod else
879b95f7d00Smiod spd_crc_cover = 125;
880b95f7d00Smiod switch (spd_len & SPDMEM_SPDLEN_MASK) {
881b95f7d00Smiod case SPDMEM_SPDLEN_128:
882b95f7d00Smiod spd_len = 128;
883b95f7d00Smiod break;
884b95f7d00Smiod case SPDMEM_SPDLEN_176:
885b95f7d00Smiod spd_len = 176;
886b95f7d00Smiod break;
887b95f7d00Smiod case SPDMEM_SPDLEN_256:
888b95f7d00Smiod spd_len = 256;
889b95f7d00Smiod break;
890b95f7d00Smiod default:
891b95f7d00Smiod return 0;
892b95f7d00Smiod }
893b9940182Sclaudio calc_crc:
894b95f7d00Smiod if (spd_crc_cover > spd_len)
895b95f7d00Smiod return 0;
896b95f7d00Smiod crc_calc = spdmem_crc16(sc, spd_crc_cover);
897b95f7d00Smiod crc_spd = spdmem_read(sc, 127) << 8;
898b95f7d00Smiod crc_spd |= spdmem_read(sc, 126);
899b95f7d00Smiod if (crc_calc != crc_spd) {
900b95f7d00Smiod return 0;
901b95f7d00Smiod }
902b95f7d00Smiod return 1;
903b9940182Sclaudio } else if (type <= SPDMEM_MEMTYPE_LPDDR4SDRAM) {
904b9940182Sclaudio spd_len = spdmem_read(sc, 0);
905b9940182Sclaudio spd_crc_cover = 125;
906b9940182Sclaudio switch (spd_len & SPDMEM_DDR4_SPDLEN_MASK) {
907b9940182Sclaudio case SPDMEM_DDR4_SPDLEN_128:
908b9940182Sclaudio spd_len = 128;
909b9940182Sclaudio break;
910b9940182Sclaudio case SPDMEM_DDR4_SPDLEN_256:
911b9940182Sclaudio spd_len = 256;
912b9940182Sclaudio break;
913b9940182Sclaudio case SPDMEM_DDR4_SPDLEN_384:
914b9940182Sclaudio spd_len = 384;
915b9940182Sclaudio break;
916b9940182Sclaudio case SPDMEM_DDR4_SPDLEN_512:
917b9940182Sclaudio spd_len = 512;
918b9940182Sclaudio break;
919b9940182Sclaudio default:
920b9940182Sclaudio return 0;
921b9940182Sclaudio }
922b9940182Sclaudio goto calc_crc;
923b95f7d00Smiod }
924b95f7d00Smiod
925b95f7d00Smiod return 0;
926b95f7d00Smiod }
927b95f7d00Smiod
928b95f7d00Smiod void
spdmem_attach_common(struct spdmem_softc * sc)929b95f7d00Smiod spdmem_attach_common(struct spdmem_softc *sc)
930b95f7d00Smiod {
931b95f7d00Smiod struct spdmem *s = &(sc->sc_spd_data);
932b95f7d00Smiod int i;
933b95f7d00Smiod
934b95f7d00Smiod /* All SPD have at least 64 bytes of data including checksum */
935b95f7d00Smiod for (i = 0; i < 64; i++) {
936b95f7d00Smiod ((uint8_t *)s)[i] = spdmem_read(sc, i);
937b95f7d00Smiod }
938b95f7d00Smiod
939b95f7d00Smiod /*
940b95f7d00Smiod * Decode and print SPD contents
941b95f7d00Smiod */
942b95f7d00Smiod if (s->sm_len < 4) {
943b95f7d00Smiod if (s->sm_type == SPDMEM_MEMTYPE_DIRECT_RAMBUS)
944b95f7d00Smiod spdmem_rdr_decode(sc, s);
945b95f7d00Smiod else
946b95f7d00Smiod printf(" no decode method for Rambus memory");
947b95f7d00Smiod } else {
948b95f7d00Smiod switch(s->sm_type) {
949b95f7d00Smiod case SPDMEM_MEMTYPE_EDO:
950b95f7d00Smiod case SPDMEM_MEMTYPE_SDRAM:
951b95f7d00Smiod spdmem_sdram_decode(sc, s);
952b95f7d00Smiod break;
953b95f7d00Smiod case SPDMEM_MEMTYPE_DDRSDRAM:
954b95f7d00Smiod spdmem_ddr_decode(sc, s);
955b95f7d00Smiod break;
956b95f7d00Smiod case SPDMEM_MEMTYPE_DDR2SDRAM:
957b95f7d00Smiod spdmem_ddr2_decode(sc, s);
958b95f7d00Smiod break;
959b95f7d00Smiod case SPDMEM_MEMTYPE_FBDIMM:
960b95f7d00Smiod case SPDMEM_MEMTYPE_FBDIMM_PROBE:
961b95f7d00Smiod spdmem_fbdimm_decode(sc, s);
962b95f7d00Smiod break;
963b95f7d00Smiod case SPDMEM_MEMTYPE_DDR3SDRAM:
964b95f7d00Smiod spdmem_ddr3_decode(sc, s);
965b95f7d00Smiod break;
966b9940182Sclaudio case SPDMEM_MEMTYPE_DDR4SDRAM:
967b9940182Sclaudio case SPDMEM_MEMTYPE_DDR4ESDRAM:
968b9940182Sclaudio case SPDMEM_MEMTYPE_LPDDR3SDRAM:
969b9940182Sclaudio case SPDMEM_MEMTYPE_LPDDR4SDRAM:
970b9940182Sclaudio spdmem_ddr4_decode(sc, s);
971b9940182Sclaudio break;
972b95f7d00Smiod case SPDMEM_MEMTYPE_NONE:
973b95f7d00Smiod printf(" no EEPROM found");
974b95f7d00Smiod break;
975b95f7d00Smiod default:
976b9940182Sclaudio if (s->sm_type <= SPDMEM_MEMTYPE_LPDDR5SDRAM)
977b95f7d00Smiod printf(" no decode method for %s memory",
978b95f7d00Smiod spdmem_basic_types[s->sm_type]);
979b95f7d00Smiod else
980b95f7d00Smiod printf(" unknown memory type %d", s->sm_type);
981b95f7d00Smiod break;
982b95f7d00Smiod }
983b95f7d00Smiod }
984b95f7d00Smiod
985b95f7d00Smiod printf("\n");
986b95f7d00Smiod }
987