summaryrefslogtreecommitdiff
path: root/arch/x86/cpu/quark/mrc.c
blob: 7eb34c530222a0c73fc4a9a4e493c5c7d237f986 (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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
 * Copyright (C) 2013, Intel Corporation
 * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
 *
 * Ported from Intel released Quark UEFI BIOS
 * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei
 *
 * SPDX-License-Identifier:	Intel
 */

/*
 * This is the main Quark Memory Reference Code (MRC)
 *
 * These functions are generic and should work for any Quark-based board.
 *
 * MRC requires two data structures to be passed in which are initialized by
 * mrc_adjust_params().
 *
 * The basic flow is as follows:
 * 01) Check for supported DDR speed configuration
 * 02) Set up Memory Manager buffer as pass-through (POR)
 * 03) Set Channel Interleaving Mode and Channel Stride to the most aggressive
 *     setting possible
 * 04) Set up the Memory Controller logic
 * 05) Set up the DDR_PHY logic
 * 06) Initialise the DRAMs (JEDEC)
 * 07) Perform the Receive Enable Calibration algorithm
 * 08) Perform the Write Leveling algorithm
 * 09) Perform the Read Training algorithm (includes internal Vref)
 * 10) Perform the Write Training algorithm
 * 11) Set Channel Interleaving Mode and Channel Stride to the desired settings
 *
 * DRAM unit configuration based on Valleyview MRC.
 */

#include <common.h>
#include <asm/arch/mrc.h>
#include <asm/arch/msg_port.h>
#include "mrc_util.h"
#include "smc.h"

static const struct mem_init init[] = {
	{ 0x0101, BM_COLD | BM_FAST | BM_WARM | BM_S3, clear_self_refresh       },
	{ 0x0200, BM_COLD | BM_FAST | BM_WARM | BM_S3, prog_ddr_timing_control  },
	{ 0x0103, BM_COLD | BM_FAST                  , prog_decode_before_jedec },
	{ 0x0104, BM_COLD | BM_FAST                  , perform_ddr_reset        },
	{ 0x0300, BM_COLD | BM_FAST           | BM_S3, ddrphy_init              },
	{ 0x0400, BM_COLD | BM_FAST                  , perform_jedec_init       },
	{ 0x0105, BM_COLD | BM_FAST                  , set_ddr_init_complete    },
	{ 0x0106,           BM_FAST | BM_WARM | BM_S3, restore_timings          },
	{ 0x0106, BM_COLD                            , default_timings          },
	{ 0x0500, BM_COLD                            , rcvn_cal                 },
	{ 0x0600, BM_COLD                            , wr_level                 },
	{ 0x0120, BM_COLD                            , prog_page_ctrl           },
	{ 0x0700, BM_COLD                            , rd_train                 },
	{ 0x0800, BM_COLD                            , wr_train                 },
	{ 0x010b, BM_COLD                            , store_timings            },
	{ 0x010c, BM_COLD | BM_FAST | BM_WARM | BM_S3, enable_scrambling        },
	{ 0x010d, BM_COLD | BM_FAST | BM_WARM | BM_S3, prog_ddr_control         },
	{ 0x010e, BM_COLD | BM_FAST | BM_WARM | BM_S3, prog_dra_drb             },
	{ 0x010f,                     BM_WARM | BM_S3, perform_wake             },
	{ 0x0110, BM_COLD | BM_FAST | BM_WARM | BM_S3, change_refresh_period    },
	{ 0x0111, BM_COLD | BM_FAST | BM_WARM | BM_S3, set_auto_refresh         },
	{ 0x0112, BM_COLD | BM_FAST | BM_WARM | BM_S3, ecc_enable               },
	{ 0x0113, BM_COLD | BM_FAST                  , memory_test              },
	{ 0x0114, BM_COLD | BM_FAST | BM_WARM | BM_S3, lock_registers           }
};

/* Adjust configuration parameters before initialization sequence */
static void mrc_adjust_params(struct mrc_params *mrc_params)
{
	const struct dram_params *dram_params;
	uint8_t dram_width;
	uint32_t rank_enables;
	uint32_t channel_width;

	ENTERFN();

	/* initially expect success */
	mrc_params->status = MRC_SUCCESS;

	dram_width = mrc_params->dram_width;
	rank_enables = mrc_params->rank_enables;
	channel_width = mrc_params->channel_width;

	/*
	 * Setup board layout (must be reviewed as is selecting static timings)
	 * 0 == R0 (DDR3 x16), 1 == R1 (DDR3 x16),
	 * 2 == DV (DDR3 x8), 3 == SV (DDR3 x8).
	 */
	if (dram_width == X8)
		mrc_params->board_id = 2;	/* select x8 layout */
	else
		mrc_params->board_id = 0;	/* select x16 layout */

	/* initially no memory */
	mrc_params->mem_size = 0;

	/* begin of channel settings */
	dram_params = &mrc_params->params;

	/*
	 * Determine column bits:
	 *
	 * Column: 11 for 8Gbx8, else 10
	 */
	mrc_params->column_bits[0] =
		((dram_params[0].density == 4) &&
		(dram_width == X8)) ? (11) : (10);

	/*
	 * Determine row bits:
	 *
	 * 512Mbx16=12 512Mbx8=13
	 * 1Gbx16=13   1Gbx8=14
	 * 2Gbx16=14   2Gbx8=15
	 * 4Gbx16=15   4Gbx8=16
	 * 8Gbx16=16   8Gbx8=16
	 */
	mrc_params->row_bits[0] = 12 + (dram_params[0].density) +
		(((dram_params[0].density < 4) &&
		(dram_width == X8)) ? (1) : (0));

	/*
	 * Determine per-channel memory size:
	 *
	 * (For 2 RANKs, multiply by 2)
	 * (For 16 bit data bus, divide by 2)
	 *
	 * DENSITY WIDTH MEM_AVAILABLE
	 * 512Mb   x16   0x008000000 ( 128MB)
	 * 512Mb   x8    0x010000000 ( 256MB)
	 * 1Gb     x16   0x010000000 ( 256MB)
	 * 1Gb     x8    0x020000000 ( 512MB)
	 * 2Gb     x16   0x020000000 ( 512MB)
	 * 2Gb     x8    0x040000000 (1024MB)
	 * 4Gb     x16   0x040000000 (1024MB)
	 * 4Gb     x8    0x080000000 (2048MB)
	 */
	mrc_params->channel_size[0] = (1 << dram_params[0].density);
	mrc_params->channel_size[0] *= (dram_width == X8) ? 2 : 1;
	mrc_params->channel_size[0] *= (rank_enables == 0x3) ? 2 : 1;
	mrc_params->channel_size[0] *= (channel_width == X16) ? 1 : 2;

	/* Determine memory size (convert number of 64MB/512Mb units) */
	mrc_params->mem_size += mrc_params->channel_size[0] << 26;

	LEAVEFN();
}

static void mrc_mem_init(struct mrc_params *mrc_params)
{
	int i;

	ENTERFN();

	/* MRC started */
	mrc_post_code(0x01, 0x00);

	if (mrc_params->boot_mode != BM_COLD) {
		if (mrc_params->ddr_speed != mrc_params->timings.ddr_speed) {
			/* full training required as frequency changed */
			mrc_params->boot_mode = BM_COLD;
		}
	}

	for (i = 0; i < ARRAY_SIZE(init); i++) {
		uint64_t my_tsc;

		if (mrc_params->boot_mode & init[i].boot_path) {
			uint8_t major = init[i].post_code >> 8 & 0xff;
			uint8_t minor = init[i].post_code >> 0 & 0xff;
			mrc_post_code(major, minor);

			my_tsc = rdtsc();
			init[i].init_fn(mrc_params);
			DPF(D_TIME, "Execution time %llx", rdtsc() - my_tsc);
		}
	}

	/* display the timings */
	print_timings(mrc_params);

	/* MRC complete */
	mrc_post_code(0x01, 0xff);

	LEAVEFN();
}

void mrc_init(struct mrc_params *mrc_params)
{
	ENTERFN();

	DPF(D_INFO, "MRC Version %04x %s %s\n", MRC_VERSION,
	    __DATE__, __TIME__);

	/* Set up the data structures used by mrc_mem_init() */
	mrc_adjust_params(mrc_params);

	/* Initialize system memory */
	mrc_mem_init(mrc_params);

	LEAVEFN();
}