meminfo.c 8.89 KB
Newer Older
Floris Bos's avatar
Floris Bos committed
1
/*
Luc Verhaegen's avatar
Luc Verhaegen committed
2
3
 * Copyright (C) 2012  Floris Bos <bos@je-eigen-domein.nl>
 * Copyright (c) 2014  Luc Verhaegen <libv@skynet.be>
Floris Bos's avatar
Floris Bos committed
4
 *
Luc Verhaegen's avatar
Luc Verhaegen committed
5
6
7
8
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
Floris Bos's avatar
Floris Bos committed
9
 *
Luc Verhaegen's avatar
Luc Verhaegen committed
10
11
12
13
14
15
16
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Floris Bos's avatar
Floris Bos committed
17
18
19
20
21
22
23
 */

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>
24
#include <errno.h>
25
#include <sys/io.h>
Floris Bos's avatar
Floris Bos committed
26
27
28

typedef uint32_t u32;

Luc Verhaegen's avatar
Luc Verhaegen committed
29
/* from u-boot code: */
Floris Bos's avatar
Floris Bos committed
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
struct dram_para {
	u32 baseaddr;
	u32 clock;
	u32 type;
	u32 rank_num;
	u32 density;
	u32 io_width;
	u32 bus_width;
	u32 cas;
	u32 zq;
	u32 odt_en;
	u32 size;
	u32 tpr0;
	u32 tpr1;
	u32 tpr2;
	u32 tpr3;
	u32 tpr4;
	u32 tpr5;
	u32 emr1;
	u32 emr2;
	u32 emr3;
};

53
54
#define DEVMEM_FILE "/dev/mem"
static int devmem_fd;
Floris Bos's avatar
Floris Bos committed
55

Luc Verhaegen's avatar
Luc Verhaegen committed
56
57
58
59
60
61
62
63
64
65
enum sunxi_soc_version {
	SUNXI_SOC_SUN4I = 0x1623, /* A10 */
	SUNXI_SOC_SUN5I = 0x1625, /* A13, A10s */
	SUNXI_SOC_SUN6I = 0x1633, /* A31 */
	SUNXI_SOC_SUN7I = 0x1651, /* A20 */
	SUNXI_SOC_SUN8I = 0x1650, /* A23 */
};

static enum sunxi_soc_version soc_version;

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
/*
 * Libv's favourite register handling calls.
 */
unsigned int
sunxi_io_read(void *base, int offset)
{
	return inl((unsigned long) (base + offset));
}

void
sunxi_io_write(void *base, int offset, unsigned int value)
{
	outl(value, (unsigned long) (base + offset));
}

void
sunxi_io_mask(void *base, int offset, unsigned int value, unsigned int mask)
{
	unsigned int tmp = inl((unsigned long) (base + offset));

	tmp &= ~mask;
	tmp |= value & mask;

	outl(tmp, (unsigned long) (base + offset));
}

Luc Verhaegen's avatar
Luc Verhaegen committed
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

/*
 * Find out exactly which SoC we are dealing with.
 */
#define SUNXI_IO_SRAM_BASE	0x01C00000
#define SUNXI_IO_SRAM_SIZE	0x00001000

#define SUNXI_IO_SRAM_VERSION	0x24

static int
soc_version_read(void)
{
	void *base;
	unsigned int restore;

	base = mmap(NULL, SUNXI_IO_SRAM_SIZE, PROT_READ|PROT_WRITE,
		    MAP_SHARED, devmem_fd, SUNXI_IO_SRAM_BASE);
	if (base == MAP_FAILED) {
		fprintf(stderr, "Failed to map sram registers: %s\n",
			strerror(errno));
		return errno;
	}

	restore = sunxi_io_read(base, SUNXI_IO_SRAM_VERSION);

	sunxi_io_mask(base, SUNXI_IO_SRAM_VERSION, 0x8000, 0x8000);

	soc_version = sunxi_io_read(base, SUNXI_IO_SRAM_VERSION) >> 16;

	sunxi_io_mask(base, SUNXI_IO_SRAM_VERSION, restore, 0x8000);

	munmap(base, SUNXI_IO_SRAM_SIZE);

	return 0;
}

static int
soc_version_check(void)
{
	switch (soc_version) {
	case SUNXI_SOC_SUN4I:
	case SUNXI_SOC_SUN5I:
	case SUNXI_SOC_SUN7I:
		return 0;
	default:
		fprintf(stderr, "Error: unknown or unhandled Soc: 0x%04X\n",
			soc_version);
		return -1;
	}
}

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
/*
 * Read DRAM clock.
 */
#define SUNXI_IO_CCM_BASE	0x01C20000
#define SUNXI_IO_CCM_SIZE	0x00001000

#define SUNXI_IO_CCM_PLL5_CFG	0x20

static int
dram_clock_read(struct dram_para *dram_para)
{
	void *base;
	unsigned int tmp;
	int n, k, m;

	base = mmap(NULL, SUNXI_IO_CCM_SIZE, PROT_READ,
		    MAP_SHARED, devmem_fd, SUNXI_IO_CCM_BASE);
	if (base == MAP_FAILED) {
		fprintf(stderr, "Failed to map ccm registers: %s\n",
			strerror(errno));
		return errno;
	}

	tmp = sunxi_io_read(base, SUNXI_IO_CCM_PLL5_CFG);

	munmap(base, SUNXI_IO_CCM_SIZE);

	n = (tmp >> 8) & 0x1F;
	k = ((tmp >> 4) & 0x03) + 1;
	m = (tmp & 0x03) + 1;

	dram_para->clock = (24 * n * k) / m;

	return 0;
}

179
180
181
182
183
184
/*
 * Read DRAM parameters.
 */
#define SUNXI_IO_DRAM_BASE	0x01C01000
#define SUNXI_IO_DRAM_SIZE	0x00001000

Luc Verhaegen's avatar
Luc Verhaegen committed
185
#define SUNXI_IO_DRAM_CCR	0x000 /* controller configuration register */
186
187
188
189
190
191
192
193
#define SUNXI_IO_DRAM_DCR	0x004 /* dram configuration */
#define SUNXI_IO_DRAM_IOCR	0x008 /* i/o configuration */

#define SUNXI_IO_DRAM_TPR0	0x014 /* dram timing parameters register 0 */
#define SUNXI_IO_DRAM_TPR1	0x018 /* dram timing parameters register 1 */
#define SUNXI_IO_DRAM_TPR2	0x01C /* dram timing parameters register 2 */

#define SUNXI_IO_DRAM_ZQCR0	0x0A8 /* zq control register 0 */
Luc Verhaegen's avatar
Luc Verhaegen committed
194
#define SUNXI_IO_DRAM_ZQCR1	0x0AC /* zq control register 1 */
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234

#define SUNXI_IO_DRAM_MR	0x1F0 /* mode register */
#define SUNXI_IO_DRAM_EMR	0x1F4 /* extended mode register */
#define SUNXI_IO_DRAM_EMR2	0x1F8 /* extended mode register */
#define SUNXI_IO_DRAM_EMR3	0x1FC /* extended mode register */

#define SUNXI_IO_DRAM_DLLCR0	0x204 /* dll control register 0(byte 0) */
#define SUNXI_IO_DRAM_DLLCR1	0x208 /* dll control register 1(byte 1) */
#define SUNXI_IO_DRAM_DLLCR2	0x20C /* dll control register 2(byte 2) */
#define SUNXI_IO_DRAM_DLLCR3	0x210 /* dll control register 3(byte 3) */
#define SUNXI_IO_DRAM_DLLCR4	0x214 /* dll control register 4(byte 4) */

static int
dram_parameters_read(struct dram_para *dram_para)
{
	void *base;
	unsigned int zqcr0, dcr;
	unsigned int dllcr0, dllcr1, dllcr2, dllcr3, dllcr4;

	base = mmap(NULL, SUNXI_IO_DRAM_SIZE, PROT_READ,
		    MAP_SHARED, devmem_fd, SUNXI_IO_DRAM_BASE);
	if (base == MAP_FAILED) {
		fprintf(stderr, "Failed to map dram registers: %s\n",
			strerror(errno));
		return errno;
	}

	dram_para->tpr0 = sunxi_io_read(base, SUNXI_IO_DRAM_TPR0);
	dram_para->tpr1 = sunxi_io_read(base, SUNXI_IO_DRAM_TPR1);
	dram_para->tpr2 = sunxi_io_read(base, SUNXI_IO_DRAM_TPR2);

	dllcr0 = (sunxi_io_read(base, SUNXI_IO_DRAM_DLLCR0) >> 6) & 0x3F;
	dllcr1 = (sunxi_io_read(base, SUNXI_IO_DRAM_DLLCR1) >> 14) & 0x0F;
	dllcr2 = (sunxi_io_read(base, SUNXI_IO_DRAM_DLLCR2) >> 14) & 0x0F;
	dllcr3 = (sunxi_io_read(base, SUNXI_IO_DRAM_DLLCR3) >> 14) & 0x0F;
	dllcr4 = (sunxi_io_read(base, SUNXI_IO_DRAM_DLLCR4) >> 14) & 0x0F;

	dram_para->tpr3 = (dllcr0 << 16) |
		(dllcr4 << 12) | (dllcr3 << 8) | (dllcr2 << 4) | dllcr1;

Luc Verhaegen's avatar
Luc Verhaegen committed
235
236
237
238
239
240
241
	if (soc_version == SUNXI_SOC_SUN7I) {
		if (sunxi_io_read(base, SUNXI_IO_DRAM_CCR) & 0x20)
			dram_para->tpr4 |= 0x01;
		if (!(sunxi_io_read(base, SUNXI_IO_DRAM_ZQCR1) & 0x01000000))
			dram_para->tpr4 |= 0x02;
	}

242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
	dram_para->cas = (sunxi_io_read(base, SUNXI_IO_DRAM_MR) >> 4) & 0x0F;
	dram_para->emr1 = sunxi_io_read(base, SUNXI_IO_DRAM_EMR);
	dram_para->emr2 = sunxi_io_read(base, SUNXI_IO_DRAM_EMR2);
	dram_para->emr3 = sunxi_io_read(base, SUNXI_IO_DRAM_EMR3);

	dram_para->odt_en = sunxi_io_read(base, SUNXI_IO_DRAM_IOCR) & 0x03;
	zqcr0 = sunxi_io_read(base, SUNXI_IO_DRAM_ZQCR0);
	dram_para->zq = (zqcr0 & 0xf0000000) |
		((zqcr0 >> 20) & 0xff) |
		((zqcr0 & 0xfffff) << 8);

	dcr = sunxi_io_read(base, SUNXI_IO_DRAM_DCR);
	if (dcr & 0x01) {
		dram_para->cas += 4;
		dram_para->type = 3;
	} else
		dram_para->type = 2;

	dram_para->density = (1 << ((dcr >> 3) & 0x07)) * 256;
	dram_para->rank_num = ((dcr >> 10) & 0x03) + 1;
	dram_para->io_width = ((dcr >> 1) & 0x03) * 8;
	dram_para->bus_width = (((dcr >> 6) & 3) + 1) * 8;

	munmap(base, SUNXI_IO_DRAM_SIZE);

	return 0;
}

Luc Verhaegen's avatar
Luc Verhaegen committed
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/*
 * Print a dram.c that can be stuck immediately into u-boot.
 */
void
dram_para_print_uboot(struct dram_para *dram_para)
{
	printf("// place this file in board/sunxi/ in u-boot\n");
	printf("/* this file is generated, don't edit it yourself */\n");
	printf("\n");
	printf("#include \"common.h\"\n");
	printf("#include <asm/arch/dram.h>\n");
	printf("\n");
	printf("static struct dram_para dram_para = {\n");
	printf("\t.clock = %d,\n", dram_para->clock);
	printf("\t.type = %d,\n", dram_para->type);
	printf("\t.rank_num = %d,\n", dram_para->rank_num);
	printf("\t.density = %d,\n", dram_para->density);
	printf("\t.io_width = %d,\n", dram_para->io_width);
	printf("\t.bus_width = %d,\n", dram_para->bus_width);
	printf("\t.cas = %d,\n", dram_para->cas);
	printf("\t.zq = 0x%02x,\n", dram_para->zq);
	printf("\t.odt_en = %d,\n", dram_para->odt_en);
	printf("\t.size = !!! FIXME !!!, /* in MiB */\n");
	printf("\t.tpr0 = 0x%08x,\n", dram_para->tpr0);
	printf("\t.tpr1 = 0x%04x,\n", dram_para->tpr1);
	printf("\t.tpr2 = 0x%05x,\n", dram_para->tpr2);
	printf("\t.tpr3 = 0x%02x,\n", dram_para->tpr3);
	printf("\t.tpr4 = 0x%02x,\n", dram_para->tpr4);
	printf("\t.tpr5 = 0x%02x,\n", dram_para->tpr5);
	printf("\t.emr1 = 0x%02x,\n", dram_para->emr1);
	printf("\t.emr2 = 0x%02x,\n", dram_para->emr2);
	printf("\t.emr3 = 0x%02x,\n", dram_para->emr3);
	printf("};\n");
	printf("\n");
	printf("unsigned long sunxi_dram_init(void)\n");
	printf("{\n");
	printf("\treturn dramc_init(&dram_para);\n");
	printf("}\n");
}
Floris Bos's avatar
Floris Bos committed
309

Luc Verhaegen's avatar
Luc Verhaegen committed
310
311
int
main(int argc, char *argv[])
Floris Bos's avatar
Floris Bos committed
312
{
Luc Verhaegen's avatar
Luc Verhaegen committed
313
	struct dram_para dram_para = {0};
314
	int ret;
315
316
317
318
319
320
321
322

	devmem_fd = open(DEVMEM_FILE, O_RDWR);
	if (devmem_fd == -1) {
		fprintf(stderr, "Error: failed to open %s: %s\n", DEVMEM_FILE,
			strerror(errno));
		return errno;
	}

Luc Verhaegen's avatar
Luc Verhaegen committed
323
324
325
326
327
328
329
	ret = soc_version_read();
	if (ret)
		return ret;
	ret = soc_version_check();
	if (ret)
		return ret;

Luc Verhaegen's avatar
Luc Verhaegen committed
330
	ret = dram_parameters_read(&dram_para);
331
332
	if (ret)
		return ret;
333

Luc Verhaegen's avatar
Luc Verhaegen committed
334
	ret = dram_clock_read(&dram_para);
335
336
	if (ret)
		return ret;
337

Luc Verhaegen's avatar
Luc Verhaegen committed
338
	dram_para_print_uboot(&dram_para);
Luc Verhaegen's avatar
Luc Verhaegen committed
339

Luc Verhaegen's avatar
Luc Verhaegen committed
340
	return 0;
Floris Bos's avatar
Floris Bos committed
341
}