aboutsummaryrefslogtreecommitdiff
path: root/src/soc/intel/common/block/memory/meminit.c
blob: 84987d612be73347e94670e5a2d6ef77013f1c12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* SPDX-License-Identifier: GPL-2.0-only */

#include <assert.h>
#include <console/console.h>
#include <intelblocks/meminit.h>
#include <commonlib/region.h>
#include <spd_bin.h>
#include <string.h>

_Static_assert(CONFIG_MRC_CHANNEL_WIDTH > 0, "MRC channel width must be >0!");
_Static_assert(CONFIG_DATA_BUS_WIDTH > 0, "Data bus width must be >0!");
_Static_assert(CONFIG_DIMMS_PER_CHANNEL > 0, "DIMMS per channel must be >0!");

/*
 * Given mask of channels that are populated, this function returns the flags
 * indicating which half of the channels are populated.
 */
static enum channel_population populated_mask_to_flag(uint32_t pop_mask, size_t max_channels)
{
	uint32_t full_mask = BIT(max_channels) - 1;
	uint32_t bottom_mask = BIT(max_channels / 2) - 1;
	uint32_t top_mask = ~bottom_mask & full_mask;

	if (pop_mask == full_mask)
		return FULLY_POPULATED;
	else if (pop_mask == bottom_mask)
		return BOTTOM_HALF_POPULATED;
	else if (pop_mask == top_mask)
		return TOP_HALF_POPULATED;
	else if (pop_mask == 0)
		return NO_CHANNEL_POPULATED;

	die("Unsupported channel population mask(0x%x)\n", pop_mask);
}

static void read_spd_md(const struct soc_mem_cfg *soc_mem_cfg, const struct mem_spd *info,
			bool half_populated, struct mem_channel_data *channel_data,
			size_t *spd_len)
{
	size_t ch;
	size_t num_phys_ch = soc_mem_cfg->num_phys_channels;
	struct region_device spd_rdev;
	uintptr_t spd_data;

	/*
	 * For memory down topologies, start with full mask as per the number
	 * of physical channels and mask out any channels based on mixed
	 * topology or half populated flag as set by the mainboard.
	 */
	uint32_t pop_mask = BIT(num_phys_ch) - 1;

	if (!(info->topo & MEM_TOPO_MEMORY_DOWN))
		return;

	if (info->topo == MEM_TOPO_MIXED)
		pop_mask &= soc_mem_cfg->md_phy_masks.mixed_topo;

	if (half_populated)
		pop_mask &= soc_mem_cfg->md_phy_masks.half_channel;

	if (pop_mask == 0)
		die("Memory technology does not support the selected configuration!\n");

	printk(BIOS_DEBUG, "SPD index = %zu\n", info->cbfs_index);

	if (get_spd_cbfs_rdev(&spd_rdev, info->cbfs_index) < 0)
		die("SPD not found in CBFS or incorrect index!\n");

	/* Memory leak is ok as long as we have memory mapped boot media */
	_Static_assert(CONFIG(BOOT_DEVICE_MEMORY_MAPPED),
		"Function assumes memory-mapped boot media");

	spd_data = (uintptr_t)rdev_mmap_full(&spd_rdev);
	*spd_len = region_device_sz(&spd_rdev);

	print_spd_info((uint8_t *)spd_data);

	for (ch = 0; ch < num_phys_ch; ch++) {
		if (!(pop_mask & BIT(ch)))
			continue;

		int mrc_ch = soc_mem_cfg->phys_to_mrc_map[ch];

		/*
		 * Memory down topology simulates a DIMM. So, the assumption is
		 * that there is a single DIMM per channel when using memory
		 * down topology. As SPD describes a DIMM, only DIMM0 for each
		 * physical channel is filled here.
		 */
		channel_data->spd[mrc_ch][0] = spd_data;
	}

	channel_data->ch_population_flags |= populated_mask_to_flag(pop_mask, num_phys_ch);
}

#define CH_DIMM_OFFSET(ch, dimm)	((ch) * CONFIG_DIMMS_PER_CHANNEL + (dimm))

static void read_spd_dimm(const struct soc_mem_cfg *soc_mem_cfg, const struct mem_spd *info,
			bool half_populated, struct mem_channel_data *channel_data,
			size_t *spd_len)
{
	size_t ch, dimm;
	struct spd_block blk = { 0 };
	size_t num_phys_ch = soc_mem_cfg->num_phys_channels;

	/*
	 * For DIMM modules, start with mask set to no channels populated. If
	 * SPD is read successfully from EEPROM for any channel, then that
	 * channel is marked as populated.
	 */
	uint32_t pop_mask = 0;

	if (!(info->topo & MEM_TOPO_DIMM_MODULE))
		return;

	for (ch = 0; ch < num_phys_ch; ch++) {
		for (dimm = 0; dimm < CONFIG_DIMMS_PER_CHANNEL; dimm++) {
			blk.addr_map[CH_DIMM_OFFSET(ch, dimm)] =
				info->smbus[ch].addr_dimm[dimm];
		}
	}

	get_spd_smbus(&blk);
	*spd_len = blk.len;

	for (ch = 0; ch < num_phys_ch; ch++) {
		size_t mrc_ch = soc_mem_cfg->phys_to_mrc_map[ch];

		for (dimm = 0; dimm < CONFIG_DIMMS_PER_CHANNEL; dimm++) {
			uint8_t *spd_data = blk.spd_array[CH_DIMM_OFFSET(ch, dimm)];
			if (spd_data == NULL)
				continue;

			print_spd_info(spd_data);

			channel_data->spd[mrc_ch][dimm] = (uintptr_t)(void *)spd_data;
			pop_mask |= BIT(ch);
		}
	}

	channel_data->ch_population_flags |= populated_mask_to_flag(pop_mask, num_phys_ch);
}

void mem_populate_channel_data(const struct soc_mem_cfg *soc_mem_cfg,
				const struct mem_spd *spd_info,
				bool half_populated,
				struct mem_channel_data *data)
{
	size_t spd_md_len = 0, spd_dimm_len = 0;

	memset(data, 0, sizeof(*data));

	read_spd_md(soc_mem_cfg, spd_info, half_populated, data, &spd_md_len);
	read_spd_dimm(soc_mem_cfg, spd_info, half_populated, data, &spd_dimm_len);

	if (data->ch_population_flags == NO_CHANNEL_POPULATED)
		die("No channels are populated. Incorrect memory configuration!\n");

	if (spd_info->topo == MEM_TOPO_MEMORY_DOWN) {
		data->spd_len = spd_md_len;
	} else if (spd_info->topo == MEM_TOPO_DIMM_MODULE) {
		data->spd_len = spd_dimm_len;
	} else {
		/*
		 * SPD lengths must match for CBFS and EEPROM SPD for mixed
		 * topology.
		 */
		if (spd_md_len != spd_dimm_len)
			die("Length of SPD does not match for mixed topology!\n");
	}
}