1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #include <console/console.h>
4 #include <spd_bin.h>
5 #include <device/smbus_def.h>
6 #include <device/smbus_host.h>
7 #include "smbuslib.h"
8
update_spd_len(struct spd_block * blk)9 static void update_spd_len(struct spd_block *blk)
10 {
11 u8 i, j = 0;
12 for (i = 0 ; i < CONFIG_DIMM_MAX; i++)
13 if (blk->spd_array[i] != NULL)
14 j |= blk->spd_array[i][SPD_DRAM_TYPE];
15
16 /* If spd used is DDR4, then its length is 512 byte. */
17 if (j == SPD_DRAM_DDR4)
18 blk->len = SPD_PAGE_LEN_DDR4;
19 else
20 blk->len = SPD_PAGE_LEN;
21 }
22
smbus_read_spd(u8 * spd,u8 addr)23 static void smbus_read_spd(u8 *spd, u8 addr)
24 {
25 u16 i;
26 u8 step = 1;
27
28 if (CONFIG(SPD_READ_BY_WORD))
29 step = sizeof(uint16_t);
30
31 for (i = 0; i < SPD_PAGE_LEN; i += step) {
32 if (CONFIG(SPD_READ_BY_WORD))
33 ((u16*)spd)[i / sizeof(uint16_t)] =
34 smbus_read_word(addr, i);
35 else
36 spd[i] = smbus_read_byte(addr, i);
37 }
38 }
39
40 /* return -1 if SMBus errors otherwise return 0 */
get_spd(u8 * spd,u8 addr)41 static int get_spd(u8 *spd, u8 addr)
42 {
43 if (CONFIG_DIMM_SPD_SIZE > SPD_PAGE_LEN) {
44 /* Restore to page 0 before reading */
45 smbus_write_byte(SPD_PAGE_0, 0, 0);
46 }
47
48 /* If address is not 0, it will return CB_ERR(-1) if no dimm */
49 if (smbus_read_byte(addr, 0) < 0) {
50 printk(BIOS_INFO, "No memory dimm at address %02X\n",
51 addr << 1);
52 return -1;
53 }
54
55 if (i2c_eeprom_read(addr, 0, SPD_PAGE_LEN, spd) < 0) {
56 printk(BIOS_INFO, "do_i2c_eeprom_read failed, using fallback\n");
57 smbus_read_spd(spd, addr);
58 }
59
60 /* Check if module is DDR4, DDR4 spd is 512 byte. */
61 if (spd[SPD_DRAM_TYPE] == SPD_DRAM_DDR4 && CONFIG_DIMM_SPD_SIZE > SPD_PAGE_LEN) {
62 /* Switch to page 1 */
63 smbus_write_byte(SPD_PAGE_1, 0, 0);
64
65 if (i2c_eeprom_read(addr, 0, SPD_PAGE_LEN, spd + SPD_PAGE_LEN) < 0) {
66 printk(BIOS_INFO, "do_i2c_eeprom_read failed, using fallback\n");
67 smbus_read_spd(spd + SPD_PAGE_LEN, addr);
68 }
69 /* Restore to page 0 */
70 smbus_write_byte(SPD_PAGE_0, 0, 0);
71 }
72 return 0;
73 }
74
75 static u8 spd_data[CONFIG_DIMM_MAX * CONFIG_DIMM_SPD_SIZE];
76
get_spd_smbus(struct spd_block * blk)77 void get_spd_smbus(struct spd_block *blk)
78 {
79 u8 i;
80 for (i = 0 ; i < CONFIG_DIMM_MAX; i++) {
81 if (blk->addr_map[i] == 0) {
82 blk->spd_array[i] = NULL;
83 continue;
84 }
85
86 if (get_spd(&spd_data[i * CONFIG_DIMM_SPD_SIZE], blk->addr_map[i]) == 0)
87 blk->spd_array[i] = &spd_data[i * CONFIG_DIMM_SPD_SIZE];
88 else
89 blk->spd_array[i] = NULL;
90 }
91
92 update_spd_len(blk);
93 }
94
95 /*
96 * get_spd_sn returns the SODIMM serial number. It only supports DDR3 and DDR4.
97 * return CB_SUCCESS, sn is the serial number and sn=0xffffffff if the dimm is not present.
98 * return CB_ERR, if dram_type is not supported or addr is a zero.
99 */
get_spd_sn(u8 addr,u32 * sn)100 enum cb_err get_spd_sn(u8 addr, u32 *sn)
101 {
102 u8 i;
103 u8 dram_type;
104 int smbus_ret;
105
106 /* addr is not a zero. */
107 if (addr == 0x0)
108 return CB_ERR;
109
110 if (CONFIG_DIMM_SPD_SIZE > SPD_PAGE_LEN) {
111 /* Restore to page 0 before reading */
112 smbus_write_byte(SPD_PAGE_0, 0, 0);
113 }
114
115 /* If dimm is not present, set sn to 0xff. */
116 smbus_ret = smbus_read_byte(addr, SPD_DRAM_TYPE);
117 if (smbus_ret < 0) {
118 printk(BIOS_INFO, "No memory dimm at address %02X\n", addr << 1);
119 *sn = 0xffffffff;
120 return CB_SUCCESS;
121 }
122
123 dram_type = smbus_ret & 0xff;
124
125 /* Check if module is DDR4, DDR4 spd is 512 byte. */
126 if (dram_type == SPD_DRAM_DDR4 && CONFIG_DIMM_SPD_SIZE > SPD_PAGE_LEN) {
127 /* Switch to page 1 */
128 smbus_write_byte(SPD_PAGE_1, 0, 0);
129
130 for (i = 0; i < SPD_SN_LEN; i++)
131 *((u8 *)sn + i) = smbus_read_byte(addr,
132 i + DDR4_SPD_SN_OFF);
133
134 /* Restore to page 0 */
135 smbus_write_byte(SPD_PAGE_0, 0, 0);
136 } else if (dram_type == SPD_DRAM_DDR3) {
137 for (i = 0; i < SPD_SN_LEN; i++)
138 *((u8 *)sn + i) = smbus_read_byte(addr,
139 i + DDR3_SPD_SN_OFF);
140 } else {
141 printk(BIOS_ERR, "Unsupported dram_type\n");
142 return CB_ERR;
143 }
144
145 return CB_SUCCESS;
146 }
147