summaryrefslogtreecommitdiff
path: root/board/tqc/tqm834x/tqm834x.c
blob: aea985ccc6c1a85476276ed34d2169e8fd7849bb (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
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
235
236
237
238
239
240
241
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
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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
/*
 * (C) Copyright 2005
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * 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.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 *
 */

#include <common.h>
#include <ioports.h>
#include <mpc83xx.h>
#include <asm/mpc8349_pci.h>
#include <i2c.h>
#include <miiphy.h>
#include <asm-ppc/mmu.h>
#include <pci.h>

DECLARE_GLOBAL_DATA_PTR;

#define IOSYNC			asm("eieio")
#define ISYNC			asm("isync")
#define SYNC			asm("sync")
#define FPW			FLASH_PORT_WIDTH
#define FPWV			FLASH_PORT_WIDTHV

#define DDR_MAX_SIZE_PER_CS	0x20000000

#if defined(DDR_CASLAT_20)
#define TIMING_CASLAT		TIMING_CFG1_CASLAT_20
#define MODE_CASLAT		DDR_MODE_CASLAT_20
#else
#define TIMING_CASLAT		TIMING_CFG1_CASLAT_25
#define MODE_CASLAT		DDR_MODE_CASLAT_25
#endif

#define INITIAL_CS_CONFIG	(CSCONFIG_EN | CSCONFIG_ROW_BIT_12 | \
				CSCONFIG_COL_BIT_9)

/* Global variable used to store detected number of banks */
int tqm834x_num_flash_banks;

/* External definitions */
ulong flash_get_size (ulong base, int banknum);
extern flash_info_t flash_info[];

/* Local functions */
static int detect_num_flash_banks(void);
static long int get_ddr_bank_size(short cs, volatile long *base);
static void set_cs_bounds(short cs, long base, long size);
static void set_cs_config(short cs, long config);
static void set_ddr_config(void);

/* Local variable */
static volatile immap_t *im = (immap_t *)CFG_IMMR;

/**************************************************************************
 * Board initialzation after relocation to RAM. Used to detect the number
 * of Flash banks on TQM834x.
 */
int board_early_init_r (void) {
	/* sanity check, IMMARBAR should be mirrored at offset zero of IMMR */
	if ((im->sysconf.immrbar & IMMRBAR_BASE_ADDR) != (u32)im)
		return 0;

	/* detect the number of Flash banks */
	return detect_num_flash_banks();
}

/**************************************************************************
 * DRAM initalization and size detection
 */
long int initdram (int board_type)
{
	long bank_size;
	long size;
	int cs;

	/* during size detection, set up the max DDRLAW size */
	im->sysconf.ddrlaw[0].bar = CFG_DDR_BASE;
	im->sysconf.ddrlaw[0].ar = (LAWAR_EN | LAWAR_SIZE_2G);

	/* set CS bounds to maximum size */
	for(cs = 0; cs < 4; ++cs) {
		set_cs_bounds(cs,
			CFG_DDR_BASE + (cs * DDR_MAX_SIZE_PER_CS),
			DDR_MAX_SIZE_PER_CS);

		set_cs_config(cs, INITIAL_CS_CONFIG);
	}

	/* configure ddr controller */
	set_ddr_config();

	udelay(200);

	/* enable DDR controller */
	im->ddr.sdram_cfg = (SDRAM_CFG_MEM_EN |
		SDRAM_CFG_SREN |
		SDRAM_CFG_SDRAM_TYPE_DDR1);
	SYNC;

	/* size detection */
	debug("\n");
	size = 0;
	for(cs = 0; cs < 4; ++cs) {
		debug("\nDetecting Bank%d\n", cs);

		bank_size = get_ddr_bank_size(cs,
			(volatile long*)(CFG_DDR_BASE + size));
		size += bank_size;

		debug("DDR Bank%d size: %d MiB\n\n", cs, bank_size >> 20);

		/* exit if less than one bank */
		if(size < DDR_MAX_SIZE_PER_CS) break;
	}

	return size;
}

/**************************************************************************
 * checkboard()
 */
int checkboard (void)
{
	puts("Board: TQM834x\n");

#ifdef CONFIG_PCI
	volatile immap_t * immr;
	u32 w, f;

	immr = (immap_t *)CFG_IMMR;
	if (!(immr->reset.rcwh & HRCWH_PCI_HOST)) {
		printf("PCI:   NOT in host mode..?!\n");
		return 0;
	}

	/* get bus width */
	w = 32;
	if (immr->reset.rcwh & HRCWH_64_BIT_PCI)
		w = 64;

	/* get clock */
	f = gd->pci_clk;

	printf("PCI1:  %d bit, %d MHz\n", w, f / 1000000);
#else
	printf("PCI:   disabled\n");
#endif
	return 0;
}


/**************************************************************************
 *
 * Local functions
 *
 *************************************************************************/

/**************************************************************************
 * Detect the number of flash banks (1 or 2). Store it in
 * a global variable tqm834x_num_flash_banks.
 * Bank detection code based on the Monitor code.
 */
static int detect_num_flash_banks(void)
{
	typedef unsigned long FLASH_PORT_WIDTH;
	typedef volatile unsigned long FLASH_PORT_WIDTHV;
	FPWV *bank1_base;
	FPWV *bank2_base;
	FPW bank1_read;
	FPW bank2_read;
	ulong bank1_size;
	ulong bank2_size;
	ulong total_size;

	tqm834x_num_flash_banks = 2;	/* assume two banks */

	/* Get bank 1 and 2 information */
	bank1_size = flash_get_size(CFG_FLASH_BASE, 0);
	debug("Bank1 size: %lu\n", bank1_size);
	bank2_size = flash_get_size(CFG_FLASH_BASE + bank1_size, 1);
	debug("Bank2 size: %lu\n", bank2_size);
	total_size = bank1_size + bank2_size;

	if (bank2_size > 0) {
		/* Seems like we've got bank 2, but maybe it's mirrored 1 */

		/* Set the base addresses */
		bank1_base = (FPWV *) (CFG_FLASH_BASE);
		bank2_base = (FPWV *) (CFG_FLASH_BASE + bank1_size);

		/* Put bank 2 into CFI command mode and read */
		bank2_base[0x55] = 0x00980098;
		IOSYNC;
		ISYNC;
		bank2_read = bank2_base[0x10];

		/* Read from bank 1 (it's in read mode) */
		bank1_read = bank1_base[0x10];

		/* Reset Flash */
		bank1_base[0] = 0x00F000F0;
		bank2_base[0] = 0x00F000F0;

		if (bank2_read == bank1_read) {
			/*
			 * Looks like just one bank, but not sure yet. Let's
			 * read from bank 2 in autosoelect mode.
			 */
			bank2_base[0x0555] = 0x00AA00AA;
			bank2_base[0x02AA] = 0x00550055;
			bank2_base[0x0555] = 0x00900090;
			IOSYNC;
			ISYNC;
			bank2_read = bank2_base[0x10];

			/* Read from bank 1 (it's in read mode) */
			bank1_read = bank1_base[0x10];

			/* Reset Flash */
			bank1_base[0] = 0x00F000F0;
			bank2_base[0] = 0x00F000F0;

			if (bank2_read == bank1_read) {
				/*
				 * In both CFI command and autoselect modes,
				 * we got the some data reading from Flash.
				 * There is only one mirrored bank.
				 */
				tqm834x_num_flash_banks = 1;
				total_size = bank1_size;
			}
		}
	}

	debug("Number of flash banks detected: %d\n", tqm834x_num_flash_banks);

	/* set OR0 and BR0 */
	im->lbus.bank[0].or = CFG_OR_TIMING_FLASH |
		(-(total_size) & OR_GPCM_AM);
	im->lbus.bank[0].br = (CFG_FLASH_BASE & BR_BA) |
		(BR_MS_GPCM | BR_PS_32 | BR_V);

	return (0);
}

/*************************************************************************
 * Detect the size of a ddr bank. Sets CS bounds and CS config accordingly.
 */
static long int get_ddr_bank_size(short cs, volatile long *base)
{
	/* This array lists all valid DDR SDRAM configurations, with
	 * Bank sizes in bytes. (Refer to Table 9-27 in the MPC8349E RM).
	 * The last entry has to to have size equal 0 and is igonred during
	 * autodection. Bank sizes must be in increasing order of size
	 */
	struct {
		long row;
		long col;
		long size;
	} conf[] = {
		{CSCONFIG_ROW_BIT_12,	CSCONFIG_COL_BIT_8,	32 << 20},
		{CSCONFIG_ROW_BIT_12,	CSCONFIG_COL_BIT_9,	64 << 20},
		{CSCONFIG_ROW_BIT_12,	CSCONFIG_COL_BIT_10,	128 << 20},
		{CSCONFIG_ROW_BIT_13,	CSCONFIG_COL_BIT_9,	128 << 20},
		{CSCONFIG_ROW_BIT_13,	CSCONFIG_COL_BIT_10,	256 << 20},
		{CSCONFIG_ROW_BIT_13,	CSCONFIG_COL_BIT_11,	512 << 20},
		{CSCONFIG_ROW_BIT_14,	CSCONFIG_COL_BIT_10,	512 << 20},
		{CSCONFIG_ROW_BIT_14,	CSCONFIG_COL_BIT_11,	1024 << 20},
		{0,			0,			0}
	};

	int i;
	int detected;
	long size;

	detected = -1;
	for(i = 0; conf[i].size != 0; ++i) {

		/* set sdram bank configuration */
		set_cs_config(cs, CSCONFIG_EN | conf[i].col | conf[i].row);

		debug("Getting RAM size...\n");
		size = get_ram_size(base, DDR_MAX_SIZE_PER_CS);

		if((size == conf[i].size) && (i == detected + 1))
			detected = i;

		debug("Trying %ld x %ld (%ld MiB) at addr %p, detected: %ld MiB\n",
			conf[i].row,
			conf[i].col,
			conf[i].size >> 20,
			base,
			size >> 20);
	}

	if(detected == -1){
		/* disable empty cs */
		debug("\nNo valid configurations for CS%d, disabling...\n", cs);
		set_cs_config(cs, 0);
		return 0;
	}

	debug("\nDetected configuration %ld x %ld (%ld MiB) at addr %p\n",
			conf[detected].row, conf[detected].col, conf[detected].size >> 20, base);

	/* configure cs ro detected params */
	set_cs_config(cs, CSCONFIG_EN | conf[detected].row |
			conf[detected].col);

	set_cs_bounds(cs, (long)base, conf[detected].size);

	return(conf[detected].size);
}

/**************************************************************************
 * Sets DDR bank CS bounds.
 */
static void set_cs_bounds(short cs, long base, long size)
{
	debug("Setting bounds %08x, %08x for cs %d\n", base, size, cs);
	if(size == 0){
		im->ddr.csbnds[cs].csbnds = 0x00000000;
	} else {
		im->ddr.csbnds[cs].csbnds =
			((base >> CSBNDS_SA_SHIFT) & CSBNDS_SA) |
			(((base + size - 1) >> CSBNDS_EA_SHIFT) &
				CSBNDS_EA);
	}
	SYNC;
}

/**************************************************************************
 * Sets DDR banks CS configuration.
 * config == 0x00000000 disables the CS.
 */
static void set_cs_config(short cs, long config)
{
	debug("Setting config %08x for cs %d\n", config, cs);
	im->ddr.cs_config[cs] = config;
	SYNC;
}

/**************************************************************************
 * Sets DDR clocks, timings and configuration.
 */
static void set_ddr_config(void) {
	/* clock control */
	im->ddr.sdram_clk_cntl = DDR_SDRAM_CLK_CNTL_SS_EN |
		DDR_SDRAM_CLK_CNTL_CLK_ADJUST_05;
	SYNC;

	/* timing configuration */
	im->ddr.timing_cfg_1 =
		(4 << TIMING_CFG1_PRETOACT_SHIFT) |
		(7 << TIMING_CFG1_ACTTOPRE_SHIFT) |
		(4 << TIMING_CFG1_ACTTORW_SHIFT)  |
		(5 << TIMING_CFG1_REFREC_SHIFT)   |
		(3 << TIMING_CFG1_WRREC_SHIFT)    |
		(3 << TIMING_CFG1_ACTTOACT_SHIFT) |
		(1 << TIMING_CFG1_WRTORD_SHIFT)   |
		(TIMING_CFG1_CASLAT & TIMING_CASLAT);

	im->ddr.timing_cfg_2 =
		TIMING_CFG2_CPO_DEF |
		(2 << TIMING_CFG2_WR_DATA_DELAY_SHIFT);
	SYNC;

	/* don't enable DDR controller yet */
	im->ddr.sdram_cfg =
		SDRAM_CFG_SREN |
		SDRAM_CFG_SDRAM_TYPE_DDR1;
	SYNC;

	/* Set SDRAM mode */
	im->ddr.sdram_mode =
		((DDR_MODE_EXT_MODEREG | DDR_MODE_WEAK) <<
			SDRAM_MODE_ESD_SHIFT) |
		((DDR_MODE_MODEREG | DDR_MODE_BLEN_4) <<
			SDRAM_MODE_SD_SHIFT) |
		((DDR_MODE_CASLAT << SDRAM_MODE_SD_SHIFT) &
			MODE_CASLAT);
	SYNC;

	/* Set fast SDRAM refresh rate */
	im->ddr.sdram_interval =
		(DDR_REFINT_166MHZ_7US << SDRAM_INTERVAL_REFINT_SHIFT) |
		(DDR_BSTOPRE << SDRAM_INTERVAL_BSTOPRE_SHIFT);
	SYNC;

	/* Workaround for DDR6 Erratum
	 * see MPC8349E Device Errata Rev.8, 2/2006
	 * This workaround influences the MPC internal "input enables"
	 * dependent on CAS latency and MPC revision. According to errata
	 * sheet the internal reserved registers for this workaround are
	 * not available from revision 2.0 and up.
	 */

	/* Get REVID from register SPRIDR. Skip workaround if rev >= 2.0
	 * (0x200)
	 */
	if ((im->sysconf.spridr & SPRIDR_REVID) < 0x200) {

		/* There is a internal reserved register at IMMRBAR+0x2F00
		 * which has to be written with a certain value defined by
		 * errata sheet.
		 */
		u32 *reserved_p = (u32 *)((u8 *)im + 0x2f00);

#if defined(DDR_CASLAT_20)
		*reserved_p = 0x201c0000;
#else
		*reserved_p = 0x202c0000;
#endif
	}
}