aboutsummaryrefslogtreecommitdiff
path: root/src/soc/intel/common/block/include/intelblocks/meminit.h
blob: cbec04ddb21612f32b9c4c87fa9f61c1502a390c (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
/* SPDX-License-Identifier: GPL-2.0-only */

#ifndef __SOC_INTEL_COMMON_BLOCK_MEMINIT_H__
#define __SOC_INTEL_COMMON_BLOCK_MEMINIT_H__

#include <stddef.h>
#include <stdint.h>
#include <types.h>

/*
 * Calculates the number of channels depending upon the data bus width of the
 * platform and the channel width.
 */
#define CHANNEL_COUNT(ch_width)		(CONFIG_DATA_BUS_WIDTH / (ch_width))

/*
 * UPDs for FSP-M are organized depending upon the MRC's view of channel. Thus,
 * the number of channels as seen by the MRC are dependent on the channel width
 * assumption in the UPDs. These channels might not necessarily be the same as
 * the physical channels supported by the platform.
 */
#define MRC_CHANNELS			CHANNEL_COUNT(CONFIG_MRC_CHANNEL_WIDTH)

/* Different memory topologies supported by the platform. */
enum mem_topology {
	MEM_TOPO_MEMORY_DOWN = BIT(0),
	MEM_TOPO_DIMM_MODULE = BIT(1),
	MEM_TOPO_MIXED = MEM_TOPO_MEMORY_DOWN | MEM_TOPO_DIMM_MODULE,
};

/*
 * SPD provides information about the memory module. Depending upon the memory
 * topology, the SPD data can be obtained from different sources. Example: for
 * memory down topology, SPD is read from CBFS using cbfs_index. For DIMM
 * modules, SPD is read from EEPROM using the DIMM addresses provided by the
 * mainboard.
 */
struct mem_spd {
	enum mem_topology topo;
	/*
	 * SPD data is read from CBFS spd.bin file using cbfs_index to locate
	 * the entry. This is used in case of MEM_TOPO_MEMORY_DOWN and
	 * MEM_TOPO_MIXED topologies.
	 */
	size_t cbfs_index;

	/*
	 * SPD data is read from on-module EEPROM using the DIMM addresses
	 * provided by the mainboard. This is used in case of
	 * MEM_TOPO_DIMM_MODULE and MEM_TOPO_MIXED topologies.
	 *
	 * Up to a maximum of MRC_CHANNELS * CONFIG_DIMMS_PER_CHANNEL addresses
	 * can be provided by mainboard. However, depending upon the memory
	 * technology being used and the number of physical channels supported
	 * by that technology, the actual channels might be less than
	 * MRC_CHANNELS.
	 */
	struct {
		uint8_t addr_dimm[CONFIG_DIMMS_PER_CHANNEL];
	} smbus[MRC_CHANNELS];
};

/* Information about memory technology supported by SoC */
struct soc_mem_cfg {
	/*
	 * Number of physical channels that are supported by the memory
	 * technology.
	 */
	size_t num_phys_channels;

	/*
	 * Map of physical channel numbers to MRC channel numbers. This is
	 * helpful in identifying what SPD entries need to be filled for a
	 * physical channel.
	 *
	 * Example: MRC supports 8 channels 0 - 7, but a memory technology
	 * supports only 2 physical channels 0 - 1. In this case, the map could
	 * be:
	 * [0] = 0,
	 * [1] = 4,
	 * indicating that physical channel 0 is mapped to MRC channel 0 and
	 * physical channel 1 is mapped to MRC channel 4.
	 */
	size_t phys_to_mrc_map[MRC_CHANNELS];

	/*
	 * Masks to be applied in case of memory down topology. For memory down
	 * topology, there is no separate EEPROM. Thus, the masks need to be
	 * hard-coded by the SoC to indicate what combinations are supported.
	 * This is a mask of physical channels for the memory technology.
	 *
	 * Example: For the memory technology supporting 2 physical channels,
	 * where the population rules restrict use of channel 0 for
	 * half-channel, half_channel mask would be set to 0x1 indicating
	 * channel 0 is always populated.
	 */
	struct {
		/*
		 * Mask of physical channels that are populated in case of
		 * half-channel configuration.
		 */
		uint32_t half_channel;
		/*
		 * Mask of physical channels that are populated with memory
		 * down parts in case of mixed topology.
		 */
		uint32_t mixed_topo;
	} md_phy_masks;
};

/* Flags indicating how the channels are populated. */
enum channel_population {
	NO_CHANNEL_POPULATED = 0,
	TOP_HALF_POPULATED = BIT(0),
	BOTTOM_HALF_POPULATED = BIT(1),
	FULLY_POPULATED = TOP_HALF_POPULATED | BOTTOM_HALF_POPULATED,
};

/*
 * Data for the memory channels that can be used by SoC code to populate FSP
 * UPDs.
 */
struct mem_channel_data {
	/* Pointer to SPD data for each DIMM of each channel */
	uintptr_t spd[MRC_CHANNELS][CONFIG_DIMMS_PER_CHANNEL];
	/* Length of SPD data */
	size_t spd_len;
	/* Flags indicating how channels are populated */
	enum channel_population ch_population_flags;
};

/*
 * This change populates data regarding memory channels in `struct
 * mem_channel_data` using the following inputs from SoC code:
 * soc_mem_cfg   : SoC-specific information about the memory technology used by
 *                 the mainboard.
 * spd_info      : Information about the memory topology.
 * half_populated: Hint from mainboard if channels are half populated.
 */
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);

/*
 * Given a channel number and the maximum number of supported channels, this
 * function returns if a channel is populated. This is useful for populating
 * DQ/DQS UPDs by the SoC code.
 */
static inline bool channel_is_populated(size_t curr_ch, size_t max_ch,
					enum channel_population flags)
{
	if ((curr_ch * 2) < max_ch)
		return !!(flags & BOTTOM_HALF_POPULATED);

	return !!(flags & TOP_HALF_POPULATED);
}

#endif /* __SOC_INTEL_COMMON_BLOCK_MEMINIT_H__ */