1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 #include <console/console.h>
5 #include <soc/meminit.h>
8 #define LPX_PHYSICAL_CH_WIDTH 16
9 #define LPX_CHANNELS CHANNEL_COUNT(LPX_PHYSICAL_CH_WIDTH)
11 #define DDR5_PHYSICAL_CH_WIDTH 32
12 #define DDR5_CHANNELS CHANNEL_COUNT(DDR5_PHYSICAL_CH_WIDTH)
14 static void set_rcomp_config(FSP_M_CONFIG
*mem_cfg
, const struct mb_cfg
*mb_cfg
)
16 if (mb_cfg
->rcomp
.resistor
!= 0)
17 mem_cfg
->RcompResistor
= mb_cfg
->rcomp
.resistor
;
19 for (size_t i
= 0; i
< ARRAY_SIZE(mem_cfg
->RcompTarget
); i
++) {
20 if (mb_cfg
->rcomp
.targets
[i
] != 0)
21 mem_cfg
->RcompTarget
[i
] = mb_cfg
->rcomp
.targets
[i
];
25 static void meminit_lp5x(FSP_M_CONFIG
*mem_cfg
, const struct mem_lp5x_config
*lp5x_config
)
27 mem_cfg
->DqPinsInterleaved
= 0;
28 mem_cfg
->Lp5CccConfig
= lp5x_config
->ccc_config
;
31 static void meminit_ddr(FSP_M_CONFIG
*mem_cfg
, const struct mem_ddr_config
*ddr_config
)
33 mem_cfg
->DqPinsInterleaved
= ddr_config
->dq_pins_interleaved
;
36 static const struct soc_mem_cfg soc_mem_cfg
[] = {
38 .num_phys_channels
= DDR5_CHANNELS
,
47 * Physical channels 0 and 1 are populated in case of
48 * half-populated configurations.
50 .half_channel
= BIT(0) | BIT(1),
51 /* In mixed topologies, channels 2 and 3 are always memory-down. */
52 .mixed_topo
= BIT(2) | BIT(3),
56 .num_phys_channels
= LPX_CHANNELS
,
69 * Physical channels 0, 1, 2 and 3 are populated in case of
70 * half-populated configurations.
72 .half_channel
= BIT(0) | BIT(1) | BIT(2) | BIT(3),
73 /* LP5x does not support mixed topologies. */
78 static void mem_init_spd_upds(FSP_M_CONFIG
*mem_cfg
, const struct mem_channel_data
*data
)
80 efi_uintn_t
*spd_upds
[MRC_CHANNELS
][CONFIG_DIMMS_PER_CHANNEL
] = {
81 [0] = { &mem_cfg
->MemorySpdPtr000
, &mem_cfg
->MemorySpdPtr001
, },
82 [1] = { &mem_cfg
->MemorySpdPtr010
, &mem_cfg
->MemorySpdPtr011
, },
83 [2] = { &mem_cfg
->MemorySpdPtr020
, &mem_cfg
->MemorySpdPtr021
, },
84 [3] = { &mem_cfg
->MemorySpdPtr030
, &mem_cfg
->MemorySpdPtr031
, },
85 [4] = { &mem_cfg
->MemorySpdPtr100
, &mem_cfg
->MemorySpdPtr101
, },
86 [5] = { &mem_cfg
->MemorySpdPtr110
, &mem_cfg
->MemorySpdPtr111
, },
87 [6] = { &mem_cfg
->MemorySpdPtr120
, &mem_cfg
->MemorySpdPtr121
, },
88 [7] = { &mem_cfg
->MemorySpdPtr130
, &mem_cfg
->MemorySpdPtr131
, },
90 uint8_t *disable_channel_upds
[MRC_CHANNELS
] = {
91 &mem_cfg
->DisableMc0Ch0
,
92 &mem_cfg
->DisableMc0Ch1
,
93 &mem_cfg
->DisableMc0Ch2
,
94 &mem_cfg
->DisableMc0Ch3
,
95 &mem_cfg
->DisableMc1Ch0
,
96 &mem_cfg
->DisableMc1Ch1
,
97 &mem_cfg
->DisableMc1Ch2
,
98 &mem_cfg
->DisableMc1Ch3
,
102 mem_cfg
->MemorySpdDataLen
= data
->spd_len
;
104 for (ch
= 0; ch
< MRC_CHANNELS
; ch
++) {
105 uint8_t *disable_channel_ptr
= disable_channel_upds
[ch
];
106 bool enable_channel
= 0;
108 for (dimm
= 0; dimm
< CONFIG_DIMMS_PER_CHANNEL
; dimm
++) {
109 efi_uintn_t
*spd_ptr
= spd_upds
[ch
][dimm
];
111 *spd_ptr
= data
->spd
[ch
][dimm
];
115 *disable_channel_ptr
= !enable_channel
;
119 static void mem_init_dq_dqs_upds(void *upds
[MRC_CHANNELS
], const void *map
, size_t upd_size
,
120 const struct mem_channel_data
*data
, bool auto_detect
)
124 for (i
= 0; i
< MRC_CHANNELS
; i
++, map
+= upd_size
) {
126 !channel_is_populated(i
, MRC_CHANNELS
, data
->ch_population_flags
))
127 memset(upds
[i
], 0, upd_size
);
129 memcpy(upds
[i
], map
, upd_size
);
133 static void mem_init_dq_upds(FSP_M_CONFIG
*mem_cfg
, const struct mem_channel_data
*data
,
134 const struct mb_cfg
*mb_cfg
, bool auto_detect
)
136 void *dq_upds
[MRC_CHANNELS
] = {
137 &mem_cfg
->DqMapCpu2DramMc0Ch0
,
138 &mem_cfg
->DqMapCpu2DramMc0Ch1
,
139 &mem_cfg
->DqMapCpu2DramMc0Ch2
,
140 &mem_cfg
->DqMapCpu2DramMc0Ch3
,
141 &mem_cfg
->DqMapCpu2DramMc1Ch0
,
142 &mem_cfg
->DqMapCpu2DramMc1Ch1
,
143 &mem_cfg
->DqMapCpu2DramMc1Ch2
,
144 &mem_cfg
->DqMapCpu2DramMc1Ch3
,
147 const size_t upd_size
= sizeof(mem_cfg
->DqMapCpu2DramMc0Ch0
);
149 _Static_assert(sizeof(mem_cfg
->DqMapCpu2DramMc0Ch0
) == CONFIG_MRC_CHANNEL_WIDTH
,
150 "Incorrect DQ UPD size!");
152 mem_init_dq_dqs_upds(dq_upds
, mb_cfg
->dq_map
, upd_size
, data
, auto_detect
);
155 static void mem_init_dqs_upds(FSP_M_CONFIG
*mem_cfg
, const struct mem_channel_data
*data
,
156 const struct mb_cfg
*mb_cfg
, bool auto_detect
)
158 void *dqs_upds
[MRC_CHANNELS
] = {
159 &mem_cfg
->DqsMapCpu2DramMc0Ch0
,
160 &mem_cfg
->DqsMapCpu2DramMc0Ch1
,
161 &mem_cfg
->DqsMapCpu2DramMc0Ch2
,
162 &mem_cfg
->DqsMapCpu2DramMc0Ch3
,
163 &mem_cfg
->DqsMapCpu2DramMc1Ch0
,
164 &mem_cfg
->DqsMapCpu2DramMc1Ch1
,
165 &mem_cfg
->DqsMapCpu2DramMc1Ch2
,
166 &mem_cfg
->DqsMapCpu2DramMc1Ch3
,
169 const size_t upd_size
= sizeof(mem_cfg
->DqsMapCpu2DramMc0Ch0
);
171 _Static_assert(sizeof(mem_cfg
->DqsMapCpu2DramMc0Ch0
) == CONFIG_MRC_CHANNEL_WIDTH
/ 8,
172 "Incorrect DQS UPD size!");
174 mem_init_dq_dqs_upds(dqs_upds
, mb_cfg
->dqs_map
, upd_size
, data
, auto_detect
);
177 #define DDR5_CH_DIMM_OFFSET(ch, dimm) ((ch) * CONFIG_DIMMS_PER_CHANNEL + (dimm))
179 static void ddr5_fill_dimm_module_info(FSP_M_CONFIG
*mem_cfg
, const struct mb_cfg
*mb_cfg
,
180 const struct mem_spd
*spd_info
)
182 for (size_t ch
= 0; ch
< soc_mem_cfg
[MEM_TYPE_DDR5
].num_phys_channels
; ch
++) {
183 for (size_t dimm
= 0; dimm
< CONFIG_DIMMS_PER_CHANNEL
; dimm
++) {
184 size_t mrc_ch
= soc_mem_cfg
[MEM_TYPE_DDR5
].phys_to_mrc_map
[ch
];
185 mem_cfg
->SpdAddressTable
[DDR5_CH_DIMM_OFFSET(mrc_ch
, dimm
)] =
186 spd_info
->smbus
[ch
].addr_dimm
[dimm
] << 1;
189 mem_init_dq_upds(mem_cfg
, NULL
, mb_cfg
, true);
190 mem_init_dqs_upds(mem_cfg
, NULL
, mb_cfg
, true);
193 void memcfg_init(FSPM_UPD
*memupd
, const struct mb_cfg
*mb_cfg
,
194 const struct mem_spd
*spd_info
, bool half_populated
)
196 struct mem_channel_data data
;
197 bool dq_dqs_auto_detect
= false;
198 FSP_M_CONFIG
*mem_cfg
= &memupd
->FspmConfig
;
200 mem_cfg
->ECT
= mb_cfg
->ect
;
201 mem_cfg
->UserBd
= mb_cfg
->UserBd
;
202 set_rcomp_config(mem_cfg
, mb_cfg
);
204 switch (mb_cfg
->type
) {
206 meminit_ddr(mem_cfg
, &mb_cfg
->ddr_config
);
207 dq_dqs_auto_detect
= true;
209 * TODO: Drop this workaround once SMBus driver in coreboot is updated to
210 * support DDR5 EEPROM reading.
212 if (spd_info
->topo
== MEM_TOPO_DIMM_MODULE
) {
213 ddr5_fill_dimm_module_info(mem_cfg
, mb_cfg
, spd_info
);
218 meminit_lp5x(mem_cfg
, &mb_cfg
->lp5x_config
);
221 die("Unsupported memory type(%d)\n", mb_cfg
->type
);
224 mem_populate_channel_data(memupd
, &soc_mem_cfg
[mb_cfg
->type
], spd_info
,
225 half_populated
, &data
);
226 mem_init_spd_upds(mem_cfg
, &data
);
227 mem_init_dq_upds(mem_cfg
, &data
, mb_cfg
, dq_dqs_auto_detect
);
228 mem_init_dqs_upds(mem_cfg
, &data
, mb_cfg
, dq_dqs_auto_detect
);