xref: /openbsd-src/sys/dev/spdmem.c (revision b7d4deb4af01579a18de515b0f53f287fe87ed7f)
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