summaryrefslogtreecommitdiff
path: root/src/soc/intel/tigerlake/meminit.c
blob: c030bb80acd6ef4cf3f58c671c0bf9ca6362f0a2 (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
172
173
174
175
176
177
/* SPDX-License-Identifier: GPL-2.0-or-later */

#include <assert.h>
#include <console/console.h>
#include <fsp/util.h>
#include <soc/meminit.h>
#include <string.h>

#define LP4X_CH_WIDTH			16
#define LP4X_CHANNELS			CHANNEL_COUNT(LP4X_CH_WIDTH)

#define DDR4_CH_WIDTH			64
#define DDR4_CHANNELS			CHANNEL_COUNT(DDR4_CH_WIDTH)

static const struct soc_mem_cfg soc_mem_cfg[] = {
	[MEM_TYPE_DDR4] = {
		.num_phys_channels = DDR4_CHANNELS,
		.phys_to_mrc_map = {
			[0] = 0,
			[1] = 4,
		},
		.md_phy_masks = {
			/*
			 * Only physical channel 0 is populated in case of half-populated
			 * configuration.
			 */
			.half_channel = BIT(0),
			/* In mixed topologies, channel 0 is always memory-down. */
			.mixed_topo = BIT(0),
		},
	},
	[MEM_TYPE_LP4X] = {
		.num_phys_channels = LP4X_CHANNELS,
		.phys_to_mrc_map = {
			[0] = 0,
			[1] = 1,
			[2] = 2,
			[3] = 3,
			[4] = 4,
			[5] = 5,
			[6] = 6,
			[7] = 7,
		},
		.md_phy_masks = {
			/*
			 * Physical channels 0, 1, 2 and 3 are populated in case of
			 * half-populated configurations.
			 */
			.half_channel = BIT(0) | BIT(1) | BIT(2) | BIT(3),
			/* LP4x does not support mixed topologies. */
		},
	},
};

static void mem_init_spd_upds(FSP_M_CONFIG *mem_cfg, const struct mem_channel_data *data)
{
	uint32_t *spd_upds[MRC_CHANNELS][CONFIG_DIMMS_PER_CHANNEL] = {
		[0] = { &mem_cfg->MemorySpdPtr00, &mem_cfg->MemorySpdPtr01, },
		[1] = { &mem_cfg->MemorySpdPtr02, &mem_cfg->MemorySpdPtr03, },
		[2] = { &mem_cfg->MemorySpdPtr04, &mem_cfg->MemorySpdPtr05, },
		[3] = { &mem_cfg->MemorySpdPtr06, &mem_cfg->MemorySpdPtr07, },
		[4] = { &mem_cfg->MemorySpdPtr08, &mem_cfg->MemorySpdPtr09, },
		[5] = { &mem_cfg->MemorySpdPtr10, &mem_cfg->MemorySpdPtr11, },
		[6] = { &mem_cfg->MemorySpdPtr12, &mem_cfg->MemorySpdPtr13, },
		[7] = { &mem_cfg->MemorySpdPtr14, &mem_cfg->MemorySpdPtr15, },
	};
	uint8_t *disable_dimm_upds[MRC_CHANNELS] = {
		&mem_cfg->DisableDimmCh0,
		&mem_cfg->DisableDimmCh1,
		&mem_cfg->DisableDimmCh2,
		&mem_cfg->DisableDimmCh3,
		&mem_cfg->DisableDimmCh4,
		&mem_cfg->DisableDimmCh5,
		&mem_cfg->DisableDimmCh6,
		&mem_cfg->DisableDimmCh7,
	};
	int ch, dimm;

	mem_cfg->MemorySpdDataLen = data->spd_len;

	for (ch = 0; ch < MRC_CHANNELS; ch++) {
		uint8_t *disable_dimm_ptr = disable_dimm_upds[ch];
		*disable_dimm_ptr = 0;

		for (dimm = 0; dimm < CONFIG_DIMMS_PER_CHANNEL; dimm++) {
			uint32_t *spd_ptr = spd_upds[ch][dimm];

			*spd_ptr = data->spd[ch][dimm];
			if (!*spd_ptr)
				*disable_dimm_ptr |= BIT(dimm);
		}
	}
}

static void mem_init_dq_dqs_upds(void *upds[MRC_CHANNELS], const void *map, size_t upd_size,
				const struct mem_channel_data *data)
{
	size_t i;

	for (i = 0; i < MRC_CHANNELS; i++, map += upd_size) {
		if (channel_is_populated(i, MRC_CHANNELS, data->ch_population_flags))
			memcpy(upds[i], map, upd_size);
		else
			memset(upds[i], 0, upd_size);
	}
}

static void mem_init_dq_upds(FSP_M_CONFIG *mem_cfg, const struct mem_channel_data *data,
				const struct mb_cfg *mb_cfg)
{
	void *dq_upds[MRC_CHANNELS] = {
		&mem_cfg->DqMapCpu2DramCh0,
		&mem_cfg->DqMapCpu2DramCh1,
		&mem_cfg->DqMapCpu2DramCh2,
		&mem_cfg->DqMapCpu2DramCh3,
		&mem_cfg->DqMapCpu2DramCh4,
		&mem_cfg->DqMapCpu2DramCh5,
		&mem_cfg->DqMapCpu2DramCh6,
		&mem_cfg->DqMapCpu2DramCh7,
	};

	const size_t upd_size = sizeof(mem_cfg->DqMapCpu2DramCh0);

	_Static_assert(upd_size == CONFIG_MRC_CHANNEL_WIDTH, "Incorrect DQ UPD size!");

	mem_init_dq_dqs_upds(dq_upds, mb_cfg->dq_map, upd_size, data);
}

static void mem_init_dqs_upds(FSP_M_CONFIG *mem_cfg, const struct mem_channel_data *data,
				const struct mb_cfg *mb_cfg)
{
	void *dqs_upds[MRC_CHANNELS] = {
		&mem_cfg->DqsMapCpu2DramCh0,
		&mem_cfg->DqsMapCpu2DramCh1,
		&mem_cfg->DqsMapCpu2DramCh2,
		&mem_cfg->DqsMapCpu2DramCh3,
		&mem_cfg->DqsMapCpu2DramCh4,
		&mem_cfg->DqsMapCpu2DramCh5,
		&mem_cfg->DqsMapCpu2DramCh6,
		&mem_cfg->DqsMapCpu2DramCh7,
	};

	const size_t upd_size = sizeof(mem_cfg->DqsMapCpu2DramCh0);

	_Static_assert(upd_size == CONFIG_MRC_CHANNEL_WIDTH / 8, "Incorrect DQS UPD size!");

	mem_init_dq_dqs_upds(dqs_upds, mb_cfg->dqs_map, upd_size, data);
}

void memcfg_init(FSP_M_CONFIG *mem_cfg, const struct mb_cfg *mb_cfg,
		 const struct mem_spd *spd_info, bool half_populated)
{
	struct mem_channel_data data;

	if (mb_cfg->type >= ARRAY_SIZE(soc_mem_cfg))
		die("Invalid memory type(%x)!\n", mb_cfg->type);

	mem_populate_channel_data(&soc_mem_cfg[mb_cfg->type], spd_info, half_populated, &data);
	mem_init_spd_upds(mem_cfg, &data);
	mem_init_dq_upds(mem_cfg, &data, mb_cfg);
	mem_init_dqs_upds(mem_cfg, &data, mb_cfg);

	mem_cfg->ECT = mb_cfg->ect;

	switch (mb_cfg->type) {
	case MEM_TYPE_DDR4:
		mem_cfg->DqPinsInterleaved = mb_cfg->ddr4_config.dq_pins_interleaved;
		break;
	case MEM_TYPE_LP4X:
		/* LPDDR4x does not allow interleaved memory */
		mem_cfg->DqPinsInterleaved = 0;
		break;
	default:
		die("Unsupported memory type(%d)\n", mb_cfg->type);
	}

}