flash.c 12.1 KB
Newer Older
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
/*
 * (C) Copyright 2001-2003
 * 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 <mpc824x.h>
#include <asm/processor.h>

28
#if defined(CONFIG_ENV_IS_IN_FLASH)
29
# ifndef  CONFIG_ENV_ADDR
30
#  define CONFIG_ENV_ADDR	(CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET)
31
# endif
32 33
# ifndef  CONFIG_ENV_SIZE
#  define CONFIG_ENV_SIZE	CONFIG_ENV_SECT_SIZE
34
# endif
35 36
# ifndef  CONFIG_ENV_SECT_SIZE
#  define CONFIG_ENV_SECT_SIZE  CONFIG_ENV_SIZE
37 38 39 40 41 42 43
# endif
#endif

#define FLASH_BANK_SIZE 0x800000
#define MAIN_SECT_SIZE  0x40000
#define PARAM_SECT_SIZE 0x8000

44
flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
45

wdenk's avatar
wdenk committed
46 47 48 49
static int write_data (flash_info_t * info, ulong dest, ulong * data);
static void write_via_fpu (vu_long * addr, ulong * data);
static __inline__ unsigned long get_msr (void);
static __inline__ void set_msr (unsigned long msr);
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

/*---------------------------------------------------------------------*/
#undef	DEBUG_FLASH

/*---------------------------------------------------------------------*/
#ifdef DEBUG_FLASH
#define DEBUGF(fmt,args...) printf(fmt ,##args)
#else
#define DEBUGF(fmt,args...)
#endif
/*---------------------------------------------------------------------*/

/*-----------------------------------------------------------------------
 */

wdenk's avatar
wdenk committed
65
unsigned long flash_init (void)
66
{
wdenk's avatar
wdenk committed
67 68 69 70
	int i, j;
	ulong size = 0;
	uchar tempChar;
	vu_long *tmpaddr;
71

wdenk's avatar
wdenk committed
72
	/* Enable flash writes on CPC45 */
73

wdenk's avatar
wdenk committed
74
	tempChar = BOARD_CTRL;
75

wdenk's avatar
wdenk committed
76
	tempChar |= (B_CTRL_FWPT_1 | B_CTRL_FWRE_1);
77

wdenk's avatar
wdenk committed
78
	tempChar &= ~(B_CTRL_FWPT_0 | B_CTRL_FWRE_0);
79

wdenk's avatar
wdenk committed
80
	BOARD_CTRL = tempChar;
81

wdenk's avatar
wdenk committed
82
	__asm__ volatile ("sync\n eieio");
83

84 85
	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
		vu_long *addr = (vu_long *) (CONFIG_SYS_FLASH_BASE + i * FLASH_BANK_SIZE);
86

wdenk's avatar
wdenk committed
87
		addr[0] = 0x00900090;
88

wdenk's avatar
wdenk committed
89
		__asm__ volatile ("sync\n eieio");
90

wdenk's avatar
wdenk committed
91
		udelay (100);
92

wdenk's avatar
wdenk committed
93 94 95 96 97 98
		DEBUGF ("Flash bank # %d:\n"
			"\tManuf. ID @ 0x%08lX: 0x%08lX\n"
			"\tDevice ID @ 0x%08lX: 0x%08lX\n",
			i,
			(ulong) (&addr[0]), addr[0],
			(ulong) (&addr[2]), addr[2]);
99 100


wdenk's avatar
wdenk committed
101 102
		if ((addr[0] == addr[1]) && (addr[0] == INTEL_MANUFACT) &&
		    (addr[2] == addr[3]) && (addr[2] == INTEL_ID_28F160F3T)) {
103

wdenk's avatar
wdenk committed
104 105 106
			flash_info[i].flash_id =
				(FLASH_MAN_INTEL & FLASH_VENDMASK) |
				(INTEL_ID_28F160F3T & FLASH_TYPEMASK);
107

wdenk's avatar
wdenk committed
108 109 110 111 112 113 114
		} else if ((addr[0] == addr[1]) && (addr[0] == INTEL_MANUFACT)
			 && (addr[2] == addr[3])
			 && (addr[2] == INTEL_ID_28F160C3T)) {

			flash_info[i].flash_id =
				(FLASH_MAN_INTEL & FLASH_VENDMASK) |
				(INTEL_ID_28F160C3T & FLASH_TYPEMASK);
115 116

		} else {
wdenk's avatar
wdenk committed
117 118 119 120 121 122 123 124 125 126
			flash_info[i].flash_id = FLASH_UNKNOWN;
			addr[0] = 0xFFFFFFFF;
			goto Done;
		}

		DEBUGF ("flash_id = 0x%08lX\n", flash_info[i].flash_id);

		addr[0] = 0xFFFFFFFF;

		flash_info[i].size = FLASH_BANK_SIZE;
127 128
		flash_info[i].sector_count = CONFIG_SYS_MAX_FLASH_SECT;
		memset (flash_info[i].protect, 0, CONFIG_SYS_MAX_FLASH_SECT);
wdenk's avatar
wdenk committed
129 130
		for (j = 0; j < flash_info[i].sector_count; j++) {
			if (j > 30) {
131
				flash_info[i].start[j] = CONFIG_SYS_FLASH_BASE +
wdenk's avatar
wdenk committed
132 133 134 135 136
					i * FLASH_BANK_SIZE +
					(MAIN_SECT_SIZE * 31) + (j -
								 31) *
					PARAM_SECT_SIZE;
			} else {
137
				flash_info[i].start[j] = CONFIG_SYS_FLASH_BASE +
wdenk's avatar
wdenk committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
					i * FLASH_BANK_SIZE +
					j * MAIN_SECT_SIZE;
			}
		}

		/* unlock sectors, if 160C3T */

		for (j = 0; j < flash_info[i].sector_count; j++) {
			tmpaddr = (vu_long *) flash_info[i].start[j];

			if ((flash_info[i].flash_id & FLASH_TYPEMASK) ==
			    (INTEL_ID_28F160C3T & FLASH_TYPEMASK)) {
				tmpaddr[0] = 0x00600060;
				tmpaddr[0] = 0x00D000D0;
				tmpaddr[1] = 0x00600060;
				tmpaddr[1] = 0x00D000D0;
			}
155
		}
wdenk's avatar
wdenk committed
156 157 158 159 160

		size += flash_info[i].size;

		addr[0] = 0x00FF00FF;
		addr[1] = 0x00FF00FF;
161 162
	}

wdenk's avatar
wdenk committed
163 164
	/* Protect monitor and environment sectors
	 */
165
#if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE + FLASH_BANK_SIZE
wdenk's avatar
wdenk committed
166
	flash_protect (FLAG_PROTECT_SET,
167 168
		       CONFIG_SYS_MONITOR_BASE,
		       CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1,
wdenk's avatar
wdenk committed
169
		       &flash_info[1]);
170
#else
wdenk's avatar
wdenk committed
171
	flash_protect (FLAG_PROTECT_SET,
172 173
		       CONFIG_SYS_MONITOR_BASE,
		       CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1,
wdenk's avatar
wdenk committed
174
		       &flash_info[0]);
175 176
#endif

177
#if defined(CONFIG_ENV_IS_IN_FLASH) && defined(CONFIG_ENV_ADDR)
178
#if CONFIG_ENV_ADDR >= CONFIG_SYS_FLASH_BASE + FLASH_BANK_SIZE
wdenk's avatar
wdenk committed
179
	flash_protect (FLAG_PROTECT_SET,
180 181
		       CONFIG_ENV_ADDR,
		       CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[1]);
182
#else
wdenk's avatar
wdenk committed
183
	flash_protect (FLAG_PROTECT_SET,
184 185
		       CONFIG_ENV_ADDR,
		       CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]);
186 187 188 189
#endif
#endif

Done:
wdenk's avatar
wdenk committed
190
	return size;
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
}

/*-----------------------------------------------------------------------
 */
void flash_print_info (flash_info_t * info)
{
	int i;

	switch ((i = info->flash_id & FLASH_VENDMASK)) {
	case (FLASH_MAN_INTEL & FLASH_VENDMASK):
		printf ("Intel: ");
		break;
	default:
		printf ("Unknown Vendor 0x%04x ", i);
		break;
	}

	switch ((i = info->flash_id & FLASH_TYPEMASK)) {
	case (INTEL_ID_28F160F3T & FLASH_TYPEMASK):
		printf ("28F160F3T (16Mbit)\n");
		break;
wdenk's avatar
wdenk committed
212 213 214 215 216

	case (INTEL_ID_28F160C3T & FLASH_TYPEMASK):
		printf ("28F160C3T (16Mbit)\n");
		break;

217 218 219 220 221 222 223
	default:
		printf ("Unknown Chip Type 0x%04x\n", i);
		goto Done;
		break;
	}

	printf ("  Size: %ld MB in %d Sectors\n",
wdenk's avatar
wdenk committed
224
		info->size >> 20, info->sector_count);
225 226 227 228 229 230 231

	printf ("  Sector Start Addresses:");
	for (i = 0; i < info->sector_count; i++) {
		if ((i % 5) == 0) {
			printf ("\n   ");
		}
		printf (" %08lX%s", info->start[i],
wdenk's avatar
wdenk committed
232
			info->protect[i] ? " (RO)" : "     ");
233 234 235 236 237 238 239 240 241 242
	}
	printf ("\n");

Done:
	return;
}

/*-----------------------------------------------------------------------
 */

wdenk's avatar
wdenk committed
243
int flash_erase (flash_info_t * info, int s_first, int s_last)
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
{
	int flag, prot, sect;
	ulong start, now, last;

	DEBUGF ("Erase flash bank %d sect %d ... %d\n",
		info - &flash_info[0], s_first, s_last);

	if ((s_first < 0) || (s_first > s_last)) {
		if (info->flash_id == FLASH_UNKNOWN) {
			printf ("- missing\n");
		} else {
			printf ("- no sectors to erase\n");
		}
		return 1;
	}

	if ((info->flash_id & FLASH_VENDMASK) !=
	    (FLASH_MAN_INTEL & FLASH_VENDMASK)) {
		printf ("Can erase only Intel flash types - aborted\n");
		return 1;
	}

	prot = 0;
wdenk's avatar
wdenk committed
267
	for (sect = s_first; sect <= s_last; ++sect) {
268 269 270 271 272 273
		if (info->protect[sect]) {
			prot++;
		}
	}

	if (prot) {
wdenk's avatar
wdenk committed
274
		printf ("- Warning: %d protected sectors will not be erased!\n", prot);
275 276 277 278 279
	} else {
		printf ("\n");
	}

	start = get_timer (0);
wdenk's avatar
wdenk committed
280
	last = start;
281
	/* Start erase on unprotected sectors */
wdenk's avatar
wdenk committed
282
	for (sect = s_first; sect <= s_last; sect++) {
283
		if (info->protect[sect] == 0) {	/* not protected */
wdenk's avatar
wdenk committed
284
			vu_long *addr = (vu_long *) (info->start[sect]);
285 286

			DEBUGF ("Erase sect %d @ 0x%08lX\n",
wdenk's avatar
wdenk committed
287
				sect, (ulong) addr);
288 289 290 291

			/* Disable interrupts which might cause a timeout
			 * here.
			 */
wdenk's avatar
wdenk committed
292
			flag = disable_interrupts ();
293 294 295 296 297 298 299 300 301 302 303

			addr[0] = 0x00500050;	/* clear status register */
			addr[0] = 0x00200020;	/* erase setup */
			addr[0] = 0x00D000D0;	/* erase confirm */

			addr[1] = 0x00500050;	/* clear status register */
			addr[1] = 0x00200020;	/* erase setup */
			addr[1] = 0x00D000D0;	/* erase confirm */

			/* re-enable interrupts if necessary */
			if (flag)
wdenk's avatar
wdenk committed
304
				enable_interrupts ();
305 306 307 308 309

			/* wait at least 80us - let's wait 1 ms */
			udelay (1000);

			while (((addr[0] & 0x00800080) != 0x00800080) ||
wdenk's avatar
wdenk committed
310 311
			       ((addr[1] & 0x00800080) != 0x00800080)) {
				if ((now = get_timer (start)) >
312
				    CONFIG_SYS_FLASH_ERASE_TOUT) {
313
					printf ("Timeout\n");
wdenk's avatar
wdenk committed
314 315
					addr[0] = 0x00B000B0;	/* suspend erase */
					addr[0] = 0x00FF00FF;	/* to read mode  */
316 317 318 319
					return 1;
				}

				/* show that we're waiting */
wdenk's avatar
wdenk committed
320
				if ((now - last) > 1000) {	/* every second  */
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
					putc ('.');
					last = now;
				}
			}

			addr[0] = 0x00FF00FF;
		}
	}
	printf (" done\n");
	return 0;
}

/*-----------------------------------------------------------------------
 * Copy memory to flash, returns:
 * 0 - OK
 * 1 - write timeout
 * 2 - Flash not erased
 * 4 - Flash not identified
 */

#define	FLASH_WIDTH	8	/* flash bus width in bytes */

wdenk's avatar
wdenk committed
343
int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
344 345 346 347 348 349 350 351
{
	ulong wp, cp, msr;
	int l, rc, i;
	ulong data[2];
	ulong *datah = &data[0];
	ulong *datal = &data[1];

	DEBUGF ("Flash write_buff: @ 0x%08lx, src 0x%08lx len %ld\n",
wdenk's avatar
wdenk committed
352
		addr, (ulong) src, cnt);
353 354 355 356 357

	if (info->flash_id == FLASH_UNKNOWN) {
		return 4;
	}

wdenk's avatar
wdenk committed
358 359
	msr = get_msr ();
	set_msr (msr | MSR_FP);
360

wdenk's avatar
wdenk committed
361
	wp = (addr & ~(FLASH_WIDTH - 1));	/* get lower aligned address */
362 363 364 365 366 367 368 369 370 371

	/*
	 * handle unaligned start bytes
	 */
	if ((l = addr - wp) != 0) {
		*datah = *datal = 0;

		for (i = 0, cp = wp; i < l; i++, cp++) {
			if (i >= 4) {
				*datah = (*datah << 8) |
wdenk's avatar
wdenk committed
372
					((*datal & 0xFF000000) >> 24);
373 374
			}

wdenk's avatar
wdenk committed
375
			*datal = (*datal << 8) | (*(uchar *) cp);
376 377
		}
		for (; i < FLASH_WIDTH && cnt > 0; ++i) {
wdenk's avatar
wdenk committed
378
			char tmp = *src++;
379 380 381

			if (i >= 4) {
				*datah = (*datah << 8) |
wdenk's avatar
wdenk committed
382
					((*datal & 0xFF000000) >> 24);
383 384 385
			}

			*datal = (*datal << 8) | tmp;
wdenk's avatar
wdenk committed
386 387
			--cnt;
			++cp;
388 389 390 391 392
		}

		for (; cnt == 0 && i < FLASH_WIDTH; ++i, ++cp) {
			if (i >= 4) {
				*datah = (*datah << 8) |
wdenk's avatar
wdenk committed
393
					((*datal & 0xFF000000) >> 24);
394 395
			}

wdenk's avatar
wdenk committed
396
			*datal = (*datah << 8) | (*(uchar *) cp);
397 398
		}

wdenk's avatar
wdenk committed
399 400
		if ((rc = write_data (info, wp, data)) != 0) {
			set_msr (msr);
401 402 403 404 405 406 407 408 409 410
			return (rc);
		}

		wp += FLASH_WIDTH;
	}

	/*
	 * handle FLASH_WIDTH aligned part
	 */
	while (cnt >= FLASH_WIDTH) {
wdenk's avatar
wdenk committed
411 412 413 414
		*datah = *(ulong *) src;
		*datal = *(ulong *) (src + 4);
		if ((rc = write_data (info, wp, data)) != 0) {
			set_msr (msr);
415 416
			return (rc);
		}
wdenk's avatar
wdenk committed
417
		wp += FLASH_WIDTH;
418 419 420 421 422
		cnt -= FLASH_WIDTH;
		src += FLASH_WIDTH;
	}

	if (cnt == 0) {
wdenk's avatar
wdenk committed
423
		set_msr (msr);
424 425 426 427 428 429 430 431
		return (0);
	}

	/*
	 * handle unaligned tail bytes
	 */
	*datah = *datal = 0;
	for (i = 0, cp = wp; i < FLASH_WIDTH && cnt > 0; ++i, ++cp) {
wdenk's avatar
wdenk committed
432
		char tmp = *src++;
433 434

		if (i >= 4) {
wdenk's avatar
wdenk committed
435 436
			*datah = (*datah << 8) | ((*datal & 0xFF000000) >>
						  24);
437 438 439 440 441 442 443 444
		}

		*datal = (*datal << 8) | tmp;
		--cnt;
	}

	for (; i < FLASH_WIDTH; ++i, ++cp) {
		if (i >= 4) {
wdenk's avatar
wdenk committed
445 446
			*datah = (*datah << 8) | ((*datal & 0xFF000000) >>
						  24);
447 448
		}

wdenk's avatar
wdenk committed
449
		*datal = (*datal << 8) | (*(uchar *) cp);
450 451
	}

wdenk's avatar
wdenk committed
452 453
	rc = write_data (info, wp, data);
	set_msr (msr);
454 455 456 457 458 459 460 461 462 463

	return (rc);
}

/*-----------------------------------------------------------------------
 * Write a word to Flash, returns:
 * 0 - OK
 * 1 - write timeout
 * 2 - Flash not erased
 */
wdenk's avatar
wdenk committed
464
static int write_data (flash_info_t * info, ulong dest, ulong * data)
465
{
wdenk's avatar
wdenk committed
466
	vu_long *addr = (vu_long *) dest;
467 468 469 470 471
	ulong start;
	int flag;

	/* Check if Flash is (sufficiently) erased */
	if (((addr[0] & data[0]) != data[0]) ||
wdenk's avatar
wdenk committed
472
	    ((addr[1] & data[1]) != data[1])) {
473 474 475
		return (2);
	}
	/* Disable interrupts which might cause a timeout here */
wdenk's avatar
wdenk committed
476
	flag = disable_interrupts ();
477

wdenk's avatar
wdenk committed
478 479
	addr[0] = 0x00400040;	/* write setup */
	write_via_fpu (addr, data);
480 481 482

	/* re-enable interrupts if necessary */
	if (flag)
wdenk's avatar
wdenk committed
483
		enable_interrupts ();
484 485 486 487

	start = get_timer (0);

	while (((addr[0] & 0x00800080) != 0x00800080) ||
wdenk's avatar
wdenk committed
488
	       ((addr[1] & 0x00800080) != 0x00800080)) {
489
		if (get_timer (start) > CONFIG_SYS_FLASH_WRITE_TOUT) {
490 491 492 493 494 495 496 497 498 499 500 501
			addr[0] = 0x00FF00FF;	/* restore read mode */
			return (1);
		}
	}

	addr[0] = 0x00FF00FF;	/* restore read mode */

	return (0);
}

/*-----------------------------------------------------------------------
 */
wdenk's avatar
wdenk committed
502
static void write_via_fpu (vu_long * addr, ulong * data)
503
{
wdenk's avatar
wdenk committed
504 505
	__asm__ __volatile__ ("lfd  1, 0(%0)"::"r" (data));
	__asm__ __volatile__ ("stfd 1, 0(%0)"::"r" (addr));
506
}
wdenk's avatar
wdenk committed
507

508 509
/*-----------------------------------------------------------------------
 */
wdenk's avatar
wdenk committed
510
static __inline__ unsigned long get_msr (void)
511
{
wdenk's avatar
wdenk committed
512 513 514
	unsigned long msr;

	__asm__ __volatile__ ("mfmsr %0":"=r" (msr):);
515

wdenk's avatar
wdenk committed
516
	return msr;
517 518
}

wdenk's avatar
wdenk committed
519
static __inline__ void set_msr (unsigned long msr)
520
{
wdenk's avatar
wdenk committed
521
	__asm__ __volatile__ ("mtmsr %0"::"r" (msr));
522
}