Angel Pons | 1b25422 | 2022-05-07 13:48:53 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | |
| 3 | #include <cbfs.h> |
| 4 | #include <commonlib/bsd/clamp.h> |
| 5 | #include <console/console.h> |
| 6 | #include <device/dram/ddr3.h> |
| 7 | #include <device/smbus_host.h> |
| 8 | #include <northbridge/intel/haswell/haswell.h> |
| 9 | #include <northbridge/intel/haswell/raminit.h> |
| 10 | #include <string.h> |
| 11 | #include <types.h> |
| 12 | |
| 13 | #include "raminit_native.h" |
| 14 | |
| 15 | static const uint8_t *get_spd_data_from_cbfs(struct spd_info *spdi) |
| 16 | { |
| 17 | if (!CONFIG(HAVE_SPD_IN_CBFS)) |
| 18 | return NULL; |
| 19 | |
| 20 | printk(RAM_DEBUG, "SPD index %u\n", spdi->spd_index); |
| 21 | |
| 22 | size_t spd_file_len; |
| 23 | uint8_t *spd_file = cbfs_map("spd.bin", &spd_file_len); |
| 24 | |
| 25 | if (!spd_file) { |
| 26 | printk(BIOS_ERR, "SPD data not found in CBFS\n"); |
| 27 | return NULL; |
| 28 | } |
| 29 | |
Elyes Haouas | ca3764a | 2024-05-12 11:50:08 +0200 | [diff] [blame] | 30 | if (spd_file_len < ((spdi->spd_index + 1) * SPD_SIZE_MAX_DDR3)) { |
Angel Pons | 1b25422 | 2022-05-07 13:48:53 +0200 | [diff] [blame] | 31 | printk(BIOS_ERR, "SPD index override to 0 - old hardware?\n"); |
| 32 | spdi->spd_index = 0; |
| 33 | } |
| 34 | |
Elyes Haouas | ca3764a | 2024-05-12 11:50:08 +0200 | [diff] [blame] | 35 | if (spd_file_len < SPD_SIZE_MAX_DDR3) { |
Angel Pons | 1b25422 | 2022-05-07 13:48:53 +0200 | [diff] [blame] | 36 | printk(BIOS_ERR, "Invalid SPD data in CBFS\n"); |
| 37 | return NULL; |
| 38 | } |
| 39 | |
Elyes Haouas | ca3764a | 2024-05-12 11:50:08 +0200 | [diff] [blame] | 40 | return spd_file + (spdi->spd_index * SPD_SIZE_MAX_DDR3); |
Angel Pons | 1b25422 | 2022-05-07 13:48:53 +0200 | [diff] [blame] | 41 | } |
| 42 | |
| 43 | static void get_spd_for_dimm(struct raminit_dimm_info *const dimm, const uint8_t *cbfs_spd) |
| 44 | { |
| 45 | if (dimm->spd_addr == SPD_MEMORY_DOWN) { |
| 46 | if (cbfs_spd) { |
Elyes Haouas | ca3764a | 2024-05-12 11:50:08 +0200 | [diff] [blame] | 47 | memcpy(dimm->raw_spd, cbfs_spd, SPD_SIZE_MAX_DDR3); |
Angel Pons | 1b25422 | 2022-05-07 13:48:53 +0200 | [diff] [blame] | 48 | dimm->valid = true; |
| 49 | printk(RAM_DEBUG, "memory-down\n"); |
| 50 | return; |
| 51 | } else { |
| 52 | printk(RAM_DEBUG, "memory-down but no CBFS SPD data, ignoring\n"); |
| 53 | return; |
| 54 | } |
| 55 | } |
| 56 | printk(RAM_DEBUG, "slotted "); |
| 57 | const uint8_t spd_mem_type = smbus_read_byte(dimm->spd_addr, SPD_MEMORY_TYPE); |
| 58 | if (spd_mem_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { |
| 59 | printk(RAM_DEBUG, "and not DDR3, ignoring\n"); |
| 60 | return; |
| 61 | } |
| 62 | printk(RAM_DEBUG, "and DDR3\n"); |
Elyes Haouas | ca3764a | 2024-05-12 11:50:08 +0200 | [diff] [blame] | 63 | if (i2c_eeprom_read(dimm->spd_addr, 0, SPD_SIZE_MAX_DDR3, dimm->raw_spd) != SPD_SIZE_MAX_DDR3) { |
Angel Pons | 1b25422 | 2022-05-07 13:48:53 +0200 | [diff] [blame] | 64 | printk(BIOS_WARNING, "I2C block read failed, trying SMBus byte reads\n"); |
Elyes Haouas | ca3764a | 2024-05-12 11:50:08 +0200 | [diff] [blame] | 65 | for (uint32_t i = 0; i < SPD_SIZE_MAX_DDR3; i++) |
Angel Pons | 1b25422 | 2022-05-07 13:48:53 +0200 | [diff] [blame] | 66 | dimm->raw_spd[i] = smbus_read_byte(dimm->spd_addr, i); |
| 67 | } |
| 68 | dimm->valid = true; |
| 69 | } |
| 70 | |
| 71 | static void get_spd_data(struct sysinfo *ctrl) |
| 72 | { |
| 73 | struct spd_info spdi = {0}; |
| 74 | mb_get_spd_map(&spdi); |
| 75 | const uint8_t *cbfs_spd = get_spd_data_from_cbfs(&spdi); |
| 76 | for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { |
| 77 | for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { |
| 78 | struct raminit_dimm_info *const dimm = &ctrl->dimms[channel][slot]; |
| 79 | dimm->spd_addr = spdi.addresses[NUM_SLOTS * channel + slot]; |
| 80 | if (!dimm->spd_addr) |
| 81 | continue; |
| 82 | |
| 83 | printk(RAM_DEBUG, "CH%uS%u is ", channel, slot); |
| 84 | get_spd_for_dimm(dimm, cbfs_spd); |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | static void decode_spd(struct raminit_dimm_info *const dimm) |
| 90 | { |
| 91 | /** TODO: Hook up somewhere, and handle lack of XMP data **/ |
| 92 | const bool enable_xmp = false; |
| 93 | memset(&dimm->data, 0, sizeof(dimm->data)); |
| 94 | if (enable_xmp) |
| 95 | spd_xmp_decode_ddr3(&dimm->data, dimm->raw_spd, DDR3_XMP_PROFILE_1); |
| 96 | else |
| 97 | spd_decode_ddr3(&dimm->data, dimm->raw_spd); |
| 98 | |
| 99 | if (CONFIG(DEBUG_RAM_SETUP)) |
| 100 | dram_print_spd_ddr3(&dimm->data); |
| 101 | } |
| 102 | |
| 103 | static enum raminit_status find_common_spd_parameters(struct sysinfo *ctrl) |
| 104 | { |
| 105 | ctrl->cas_supported = 0xffff; |
| 106 | ctrl->flags.raw = 0xffffffff; |
| 107 | |
| 108 | ctrl->tCK = 0; |
| 109 | ctrl->tAA = 0; |
| 110 | ctrl->tWR = 0; |
| 111 | ctrl->tRCD = 0; |
| 112 | ctrl->tRRD = 0; |
| 113 | ctrl->tRP = 0; |
| 114 | ctrl->tRAS = 0; |
| 115 | ctrl->tRC = 0; |
| 116 | ctrl->tRFC = 0; |
| 117 | ctrl->tWTR = 0; |
| 118 | ctrl->tRTP = 0; |
| 119 | ctrl->tFAW = 0; |
| 120 | ctrl->tCWL = 0; |
| 121 | ctrl->tCMD = 0; |
| 122 | ctrl->chanmap = 0; |
| 123 | |
| 124 | bool yes_ecc = false; |
| 125 | bool not_ecc = false; |
| 126 | |
| 127 | for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { |
| 128 | ctrl->dpc[channel] = 0; |
| 129 | ctrl->rankmap[channel] = 0; |
| 130 | ctrl->rank_mirrored[channel] = 0; |
| 131 | ctrl->channel_size_mb[channel] = 0; |
| 132 | for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { |
| 133 | struct raminit_dimm_info *const dimm = &ctrl->dimms[channel][slot]; |
| 134 | if (!dimm->valid) |
| 135 | continue; |
| 136 | |
| 137 | printk(RAM_DEBUG, "\nCH%uS%u SPD:\n", channel, slot); |
| 138 | decode_spd(dimm); |
| 139 | |
| 140 | ctrl->chanmap |= BIT(channel); |
| 141 | ctrl->dpc[channel]++; |
| 142 | ctrl->channel_size_mb[channel] += dimm->data.size_mb; |
| 143 | |
| 144 | /* The first rank of a populated slot is always present */ |
| 145 | const uint8_t rank = slot + slot; |
| 146 | assert(dimm->data.ranks); |
| 147 | ctrl->rankmap[channel] |= (BIT(dimm->data.ranks) - 1) << rank; |
| 148 | |
| 149 | if (dimm->data.flags.pins_mirrored) |
| 150 | ctrl->rank_mirrored[channel] |= BIT(rank + 1); |
| 151 | |
| 152 | /* Find common settings */ |
| 153 | ctrl->cas_supported &= dimm->data.cas_supported; |
| 154 | ctrl->flags.raw &= dimm->data.flags.raw; |
| 155 | ctrl->tCK = MAX(ctrl->tCK, dimm->data.tCK); |
| 156 | ctrl->tAA = MAX(ctrl->tAA, dimm->data.tAA); |
| 157 | ctrl->tWR = MAX(ctrl->tWR, dimm->data.tWR); |
| 158 | ctrl->tRCD = MAX(ctrl->tRCD, dimm->data.tRCD); |
| 159 | ctrl->tRRD = MAX(ctrl->tRRD, dimm->data.tRRD); |
| 160 | ctrl->tRP = MAX(ctrl->tRP, dimm->data.tRP); |
| 161 | ctrl->tRAS = MAX(ctrl->tRAS, dimm->data.tRAS); |
| 162 | ctrl->tRC = MAX(ctrl->tRC, dimm->data.tRC); |
| 163 | ctrl->tRFC = MAX(ctrl->tRFC, dimm->data.tRFC); |
| 164 | ctrl->tWTR = MAX(ctrl->tWTR, dimm->data.tWTR); |
| 165 | ctrl->tRTP = MAX(ctrl->tRTP, dimm->data.tRTP); |
| 166 | ctrl->tFAW = MAX(ctrl->tFAW, dimm->data.tFAW); |
| 167 | ctrl->tCWL = MAX(ctrl->tCWL, dimm->data.tCWL); |
| 168 | ctrl->tCMD = MAX(ctrl->tCMD, dimm->data.tCMD); |
| 169 | |
| 170 | yes_ecc |= dimm->data.flags.is_ecc; |
| 171 | not_ecc |= !dimm->data.flags.is_ecc; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | if (!ctrl->chanmap) { |
| 176 | printk(BIOS_ERR, "No DIMMs were found\n"); |
| 177 | return RAMINIT_STATUS_NO_MEMORY_INSTALLED; |
| 178 | } |
| 179 | if (!ctrl->cas_supported) { |
| 180 | printk(BIOS_ERR, "Could not resolve common CAS latency\n"); |
| 181 | return RAMINIT_STATUS_UNSUPPORTED_MEMORY; |
| 182 | } |
| 183 | /** TODO: Properly handle ECC support and ECC forced **/ |
| 184 | if (yes_ecc && not_ecc) { |
| 185 | /** TODO: Test if the ECC DIMMs can be operated as non-ECC DIMMs **/ |
| 186 | printk(BIOS_ERR, "Both ECC and non-ECC DIMMs present, this is unsupported\n"); |
| 187 | return RAMINIT_STATUS_UNSUPPORTED_MEMORY; |
| 188 | } |
| 189 | if (yes_ecc) |
| 190 | ctrl->lanes = NUM_LANES; |
| 191 | else |
| 192 | ctrl->lanes = NUM_LANES_NO_ECC; |
| 193 | |
| 194 | ctrl->is_ecc = yes_ecc; |
| 195 | |
| 196 | /** TODO: Complete LPDDR support **/ |
| 197 | ctrl->lpddr = false; |
| 198 | |
| 199 | return RAMINIT_STATUS_SUCCESS; |
| 200 | } |
| 201 | |
| 202 | enum raminit_status collect_spd_info(struct sysinfo *ctrl) |
| 203 | { |
| 204 | get_spd_data(ctrl); |
| 205 | return find_common_spd_parameters(ctrl); |
| 206 | } |