flash.c 20.7 KB
Newer Older
W
wdenk 已提交
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
/*
 * (C) 2000 Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 * (C) 2003 August Hoeraendl, Logotronic GmbH
 *
 * 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
 */

#undef CONFIG_FLASH_16BIT

#include <common.h>

#define FLASH_BANK_SIZE 0x1000000
29
#define MAIN_SECT_SIZE  0x20000		/* 2x64k = 128k per sector */
W
wdenk 已提交
30

31
flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips	*/
W
wdenk 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

/* NOTE - CONFIG_FLASH_16BIT means the CPU interface is 16-bit, it
 *        has nothing to do with the flash chip being 8-bit or 16-bit.
 */
#ifdef CONFIG_FLASH_16BIT
typedef unsigned short FLASH_PORT_WIDTH;
typedef volatile unsigned short FLASH_PORT_WIDTHV;

#define	FLASH_ID_MASK	0xFFFF
#else
typedef unsigned long FLASH_PORT_WIDTH;
typedef volatile unsigned long FLASH_PORT_WIDTHV;

#define	FLASH_ID_MASK	0xFFFFFFFF
#endif

#define FPW	FLASH_PORT_WIDTH
#define FPWV	FLASH_PORT_WIDTHV

#define ORMASK(size) ((-size) & OR_AM_MSK)

/*-----------------------------------------------------------------------
 * Functions
 */
56 57 58 59
static ulong flash_get_size(FPWV *addr, flash_info_t *info);
static void flash_reset(flash_info_t *info);
static int write_word_intel(flash_info_t *info, FPWV *dest, FPW data);
static int write_word_amd(flash_info_t *info, FPWV *dest, FPW data);
W
wdenk 已提交
60
#define write_word(in, de, da)   write_word_amd(in, de, da)
61
static void flash_get_offsets(ulong base, flash_info_t *info);
W
wdenk 已提交
62
#ifdef CFG_FLASH_PROTECTION
63
static void flash_sync_real_protect(flash_info_t *info);
W
wdenk 已提交
64 65 66 67 68 69 70
#endif

/*-----------------------------------------------------------------------
 * flash_init()
 *
 * sets up flash_info and returns size of FLASH (bytes)
 */
71
ulong flash_init(void)
W
wdenk 已提交
72
{
73 74 75 76 77 78 79 80 81 82 83 84
    int i, j;
    ulong size = 0;

    for (i = 0; i < CFG_MAX_FLASH_BANKS; i++)
    {
	ulong flashbase = 0;
	flash_info[i].flash_id =
	  (FLASH_MAN_AMD & FLASH_VENDMASK) |
	  (FLASH_AM640U & FLASH_TYPEMASK);
	flash_info[i].size = FLASH_BANK_SIZE;
	flash_info[i].sector_count = CFG_MAX_FLASH_SECT;
	memset(flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
W
wdenk 已提交
85 86 87 88 89 90 91 92 93
	switch (i)
	{
	   case 0:
		flashbase = PHYS_FLASH_1;
		break;
	   case 1:
		flashbase = PHYS_FLASH_2;
		break;
	   default:
W
wdenk 已提交
94
		panic("configured too many flash banks!\n");
W
wdenk 已提交
95 96
		break;
	}
97 98 99
	for (j = 0; j < flash_info[i].sector_count; j++)
	{
	    flash_info[i].start[j] = flashbase + j*MAIN_SECT_SIZE;
W
wdenk 已提交
100
	}
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
	size += flash_info[i].size;
    }

    /* Protect monitor and environment sectors
     */
    flash_protect(FLAG_PROTECT_SET,
		  CFG_FLASH_BASE,
		  CFG_FLASH_BASE + _armboot_end_data - _armboot_start,
		  &flash_info[0]);

    flash_protect(FLAG_PROTECT_SET,
		  CFG_ENV_ADDR,
		  CFG_ENV_ADDR + CFG_ENV_SIZE - 1,
		  &flash_info[0]);

    return size;
W
wdenk 已提交
117 118 119 120
}

/*-----------------------------------------------------------------------
 */
121
static void flash_reset(flash_info_t *info)
W
wdenk 已提交
122
{
123
	FPWV *base = (FPWV *)(info->start[0]);
W
wdenk 已提交
124 125 126

	/* Put FLASH back in read mode */
	if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL)
127
		*base = (FPW)0x00FF00FF;	/* Intel Read Mode */
W
wdenk 已提交
128
	else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD)
129
		*base = (FPW)0x00F000F0;	/* AMD Read Mode */
W
wdenk 已提交
130 131 132 133
}

/*-----------------------------------------------------------------------
 */
134
static void flash_get_offsets (ulong base, flash_info_t *info)
W
wdenk 已提交
135 136 137 138 139
{
	int i;

	/* set up sector start address table */
	if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL
140 141
	    && (info->flash_id & FLASH_BTYPE)) {
		int bootsect_size;	/* number of bytes/boot sector	*/
W
wdenk 已提交
142 143
		int sect_size;		/* number of bytes/regular sector */

144 145
		bootsect_size = 0x00002000 * (sizeof(FPW)/2);
		sect_size =     0x00010000 * (sizeof(FPW)/2);
W
wdenk 已提交
146

147
		/* set sector offsets for bottom boot block type	*/
W
wdenk 已提交
148 149 150 151 152 153
		for (i = 0; i < 8; ++i) {
			info->start[i] = base + (i * bootsect_size);
		}
		for (i = 8; i < info->sector_count; i++) {
			info->start[i] = base + ((i - 7) * sect_size);
		}
154 155 156
	}
	else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD
		 && (info->flash_id & FLASH_TYPEMASK) == FLASH_AM640U) {
W
wdenk 已提交
157 158 159

		int sect_size;		/* number of bytes/sector */

160
		sect_size = 0x00010000 * (sizeof(FPW)/2);
W
wdenk 已提交
161 162

		/* set up sector start address table (uniform sector type) */
163
		for( i = 0; i < info->sector_count; i++ )
W
wdenk 已提交
164 165 166 167 168 169 170
			info->start[i] = base + (i * sect_size);
	}
}

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

171
void flash_print_info (flash_info_t *info)
W
wdenk 已提交
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
{
	int i;
	uchar *boottype;
	uchar *bootletter;
	uchar *fmt;
	uchar botbootletter[] = "B";
	uchar topbootletter[] = "T";
	uchar botboottype[] = "bottom boot sector";
	uchar topboottype[] = "top boot sector";

	if (info->flash_id == FLASH_UNKNOWN) {
		printf ("missing or unknown FLASH type\n");
		return;
	}

	switch (info->flash_id & FLASH_VENDMASK) {
188 189 190 191 192 193 194
	case FLASH_MAN_AMD:	printf ("AMD ");		break;
	case FLASH_MAN_BM:	printf ("BRIGHT MICRO ");	break;
	case FLASH_MAN_FUJ:	printf ("FUJITSU ");		break;
	case FLASH_MAN_SST:	printf ("SST ");		break;
	case FLASH_MAN_STM:	printf ("STM ");		break;
	case FLASH_MAN_INTEL:	printf ("INTEL ");		break;
	default:		printf ("Unknown Vendor ");	break;
W
wdenk 已提交
195 196 197 198 199 200
	}

	/* check for top or bottom boot, if it applies */
	if (info->flash_id & FLASH_BTYPE) {
		boottype = botboottype;
		bootletter = botbootletter;
201 202
	}
	else {
W
wdenk 已提交
203 204 205 206 207 208 209 210
		boottype = topboottype;
		bootletter = topbootletter;
	}

	switch (info->flash_id & FLASH_TYPEMASK) {
	case FLASH_AM640U:
		fmt = "29LV641D (64 Mbit, uniform sectors)\n";
		break;
W
wdenk 已提交
211 212
	case FLASH_28F800C3B:
	case FLASH_28F800C3T:
W
wdenk 已提交
213 214 215 216 217 218
		fmt = "28F800C3%s (8 Mbit, %s)\n";
		break;
	case FLASH_INTEL800B:
	case FLASH_INTEL800T:
		fmt = "28F800B3%s (8 Mbit, %s)\n";
		break;
W
wdenk 已提交
219 220
	case FLASH_28F160C3B:
	case FLASH_28F160C3T:
W
wdenk 已提交
221 222 223 224 225 226
		fmt = "28F160C3%s (16 Mbit, %s)\n";
		break;
	case FLASH_INTEL160B:
	case FLASH_INTEL160T:
		fmt = "28F160B3%s (16 Mbit, %s)\n";
		break;
W
wdenk 已提交
227 228
	case FLASH_28F320C3B:
	case FLASH_28F320C3T:
W
wdenk 已提交
229 230 231 232 233 234
		fmt = "28F320C3%s (32 Mbit, %s)\n";
		break;
	case FLASH_INTEL320B:
	case FLASH_INTEL320T:
		fmt = "28F320B3%s (32 Mbit, %s)\n";
		break;
W
wdenk 已提交
235 236
	case FLASH_28F640C3B:
	case FLASH_28F640C3T:
W
wdenk 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249 250
		fmt = "28F640C3%s (64 Mbit, %s)\n";
		break;
	case FLASH_INTEL640B:
	case FLASH_INTEL640T:
		fmt = "28F640B3%s (64 Mbit, %s)\n";
		break;
	default:
		fmt = "Unknown Chip Type\n";
		break;
	}

	printf (fmt, bootletter, boottype);

	printf ("  Size: %ld MB in %d Sectors\n",
251 252
		info->size >> 20,
		info->sector_count);
W
wdenk 已提交
253 254 255

	printf ("  Sector Start Addresses:");

256
	for (i=0; i<info->sector_count; ++i) {
W
wdenk 已提交
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
		if ((i % 5) == 0) {
			printf ("\n   ");
		}

		printf (" %08lX%s", info->start[i],
			info->protect[i] ? " (RO)" : "     ");
	}

	printf ("\n");
}

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

/*
 * The following code cannot be run from FLASH!
 */

275
ulong flash_get_size (FPWV *addr, flash_info_t *info)
W
wdenk 已提交
276 277 278 279
{
	/* Write auto select command: read Manufacturer ID */

	/* Write auto select command sequence and test FLASH answer */
280 281 282
	addr[0x0555] = (FPW)0x00AA00AA;	/* for AMD, Intel ignores this */
	addr[0x02AA] = (FPW)0x00550055;	/* for AMD, Intel ignores this */
	addr[0x0555] = (FPW)0x00900090;	/* selects Intel or AMD */
W
wdenk 已提交
283 284 285 286 287 288

	/* The manufacturer codes are only 1 byte, so just use 1 byte.
	 * This works for any bus width and any FLASH device width.
	 */
	switch (addr[0] & 0xff) {

289
	case (uchar)AMD_MANUFACT:
W
wdenk 已提交
290 291 292
		info->flash_id = FLASH_MAN_AMD;
		break;

293
	case (uchar)INTEL_MANUFACT:
W
wdenk 已提交
294 295 296 297 298 299 300 301 302 303 304
		info->flash_id = FLASH_MAN_INTEL;
		break;

	default:
		info->flash_id = FLASH_UNKNOWN;
		info->sector_count = 0;
		info->size = 0;
		break;
	}

	/* Check 16 bits or 32 bits of ID so work on 32 or 16 bit bus. */
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
	if (info->flash_id != FLASH_UNKNOWN) switch (addr[1]) {

	case (FPW)AMD_ID_LV640U:	/* 29LV640 and 29LV641 have same ID */
		info->flash_id += FLASH_AM640U;
		info->sector_count = 128;
		info->size = 0x00800000 * (sizeof(FPW)/2);
		break;				/* => 8 or 16 MB	*/

	case (FPW)INTEL_ID_28F800C3B:
		info->flash_id += FLASH_28F800C3B;
		info->sector_count = 23;
		info->size = 0x00100000 * (sizeof(FPW)/2);
		break;				/* => 1 or 2 MB		*/

	case (FPW)INTEL_ID_28F800B3B:
		info->flash_id += FLASH_INTEL800B;
		info->sector_count = 23;
		info->size = 0x00100000 * (sizeof(FPW)/2);
		break;				/* => 1 or 2 MB		*/

	case (FPW)INTEL_ID_28F160C3B:
		info->flash_id += FLASH_28F160C3B;
		info->sector_count = 39;
		info->size = 0x00200000 * (sizeof(FPW)/2);
		break;				/* => 2 or 4 MB		*/

	case (FPW)INTEL_ID_28F160B3B:
		info->flash_id += FLASH_INTEL160B;
		info->sector_count = 39;
		info->size = 0x00200000 * (sizeof(FPW)/2);
		break;				/* => 2 or 4 MB		*/

	case (FPW)INTEL_ID_28F320C3B:
		info->flash_id += FLASH_28F320C3B;
		info->sector_count = 71;
		info->size = 0x00400000 * (sizeof(FPW)/2);
		break;				/* => 4 or 8 MB		*/

	case (FPW)INTEL_ID_28F320B3B:
		info->flash_id += FLASH_INTEL320B;
		info->sector_count = 71;
		info->size = 0x00400000 * (sizeof(FPW)/2);
		break;				/* => 4 or 8 MB		*/

	case (FPW)INTEL_ID_28F640C3B:
		info->flash_id += FLASH_28F640C3B;
		info->sector_count = 135;
		info->size = 0x00800000 * (sizeof(FPW)/2);
		break;				/* => 8 or 16 MB	*/

	case (FPW)INTEL_ID_28F640B3B:
		info->flash_id += FLASH_INTEL640B;
		info->sector_count = 135;
		info->size = 0x00800000 * (sizeof(FPW)/2);
		break;				/* => 8 or 16 MB	*/
W
wdenk 已提交
360

361 362 363 364 365 366 367 368
	default:
		info->flash_id = FLASH_UNKNOWN;
		info->sector_count = 0;
		info->size = 0;
		return (0);			/* => no or unknown flash */
	}

	flash_get_offsets((ulong)addr, info);
W
wdenk 已提交
369 370

	/* Put FLASH back in read mode */
371
	flash_reset(info);
W
wdenk 已提交
372 373 374 375 376 377 378 379

	return (info->size);
}

#ifdef CFG_FLASH_PROTECTION
/*-----------------------------------------------------------------------
 */

380
static void flash_sync_real_protect(flash_info_t *info)
W
wdenk 已提交
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
    FPWV *addr = (FPWV *)(info->start[0]);
    FPWV *sect;
    int i;

    switch (info->flash_id & FLASH_TYPEMASK) {
    case FLASH_28F800C3B:
    case FLASH_28F800C3T:
    case FLASH_28F160C3B:
    case FLASH_28F160C3T:
    case FLASH_28F320C3B:
    case FLASH_28F320C3T:
    case FLASH_28F640C3B:
    case FLASH_28F640C3T:
	/* check for protected sectors */
	*addr = (FPW)0x00900090;
	for (i = 0; i < info->sector_count; i++) {
	    /* read sector protection at sector address, (A7 .. A0) = 0x02.
	     * D0 = 1 for each device if protected.
	     * If at least one device is protected the sector is marked
	     * protected, but mixed protected and  unprotected devices
	     * within a sector should never happen.
	     */
	    sect = (FPWV *)(info->start[i]);
	    info->protect[i] = (sect[2] & (FPW)(0x00010001)) ? 1 : 0;
W
wdenk 已提交
406
	}
407 408 409 410 411 412 413 414 415 416

	/* Put FLASH back in read mode */
	flash_reset(info);
	break;

    case FLASH_AM640U:
    default:
	/* no hardware protect that we support */
	break;
    }
W
wdenk 已提交
417 418 419 420 421 422
}
#endif

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

423
int	flash_erase (flash_info_t *info, int s_first, int s_last)
W
wdenk 已提交
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
{
	FPWV *addr;
	int flag, prot, sect;
	int intel = (info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL;
	ulong start, now, last;
	int rcode = 0;

	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;
	}

	switch (info->flash_id & FLASH_TYPEMASK) {
	case FLASH_INTEL800B:
	case FLASH_INTEL160B:
	case FLASH_INTEL320B:
	case FLASH_INTEL640B:
	case FLASH_28F800C3B:
	case FLASH_28F160C3B:
	case FLASH_28F320C3B:
	case FLASH_28F640C3B:
	case FLASH_AM640U:
		break;
	case FLASH_UNKNOWN:
	default:
		printf ("Can't erase unknown flash type %08lx - aborted\n",
			info->flash_id);
		return 1;
	}

	prot = 0;
459
	for (sect=s_first; sect<=s_last; ++sect) {
W
wdenk 已提交
460 461 462 463 464 465 466
		if (info->protect[sect]) {
			prot++;
		}
	}

	if (prot) {
		printf ("- Warning: %d protected sectors will not be erased!\n",
467
			prot);
W
wdenk 已提交
468 469 470 471
	} else {
		printf ("\n");
	}

472 473
	start = get_timer(0);
	last  = start;
W
wdenk 已提交
474 475

	/* Start erase on unprotected sectors */
476
	for (sect = s_first; sect<=s_last && rcode == 0; sect++) {
W
wdenk 已提交
477 478 479 480 481

		if (info->protect[sect] != 0)	/* protected, skip it */
			continue;

		/* Disable interrupts which might cause a timeout here */
482
		flag = disable_interrupts();
W
wdenk 已提交
483

484
		addr = (FPWV *)(info->start[sect]);
W
wdenk 已提交
485
		if (intel) {
486 487 488 489 490
			*addr = (FPW)0x00500050; /* clear status register */
			*addr = (FPW)0x00200020; /* erase setup */
			*addr = (FPW)0x00D000D0; /* erase confirm */
		}
		else {
W
wdenk 已提交
491
			/* must be AMD style if not Intel */
492 493 494 495 496 497 498 499 500
			FPWV *base;		/* first address in bank */

			base = (FPWV *)(info->start[0]);
			base[0x0555] = (FPW)0x00AA00AA;	/* unlock */
			base[0x02AA] = (FPW)0x00550055;	/* unlock */
			base[0x0555] = (FPW)0x00800080;	/* erase mode */
			base[0x0555] = (FPW)0x00AA00AA;	/* unlock */
			base[0x02AA] = (FPW)0x00550055;	/* unlock */
			*addr = (FPW)0x00300030;	/* erase sector */
W
wdenk 已提交
501 502 503 504
		}

		/* re-enable interrupts if necessary */
		if (flag)
505
			enable_interrupts();
W
wdenk 已提交
506 507 508 509 510 511

		/* wait at least 50us for AMD, 80us for Intel.
		 * Let's wait 1 ms.
		 */
		udelay (1000);

512 513
		while ((*addr & (FPW)0x00800080) != (FPW)0x00800080) {
			if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) {
W
wdenk 已提交
514 515 516
				printf ("Timeout\n");

				if (intel) {
517 518
					/* suspend erase	*/
					*addr = (FPW)0x00B000B0;
W
wdenk 已提交
519 520
				}

521
				flash_reset(info);	/* reset to read mode */
W
wdenk 已提交
522 523 524 525 526 527 528 529 530 531 532
				rcode = 1;		/* failed */
				break;
			}

			/* show that we're waiting */
			if ((now - last) > 1000) {	/* every second */
				putc ('.');
				last = now;
			}
		}

533
		flash_reset(info);	/* reset to read mode	*/
W
wdenk 已提交
534 535 536 537 538 539 540 541 542 543 544 545 546 547
	}

	printf (" done\n");
	return rcode;
}

/*-----------------------------------------------------------------------
 * Copy memory to flash, returns:
 * 0 - OK
 * 1 - write timeout
 * 2 - Flash not erased
 */
int bad_write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt)
{
548 549 550 551
    FPW data = 0; /* 16 or 32 bit word, matches flash bus width on MPC8XX */
    int bytes;	  /* number of bytes to program in current word		*/
    int left;	  /* number of bytes left to program			*/
    int i, res;
W
wdenk 已提交
552

553 554 555
    for (left = cnt, res = 0;
	 left > 0 && res == 0;
	 addr += sizeof(data), left -= sizeof(data) - bytes) {
W
wdenk 已提交
556

W
wdenk 已提交
557 558
	bytes = addr & (sizeof(data) - 1);
	addr &= ~(sizeof(data) - 1);
W
wdenk 已提交
559

560 561 562
	/* combine source and destination data so can program
	 * an entire word of 16 or 32 bits
	 */
W
wdenk 已提交
563 564 565
	for (i = 0; i < sizeof(data); i++) {
	    data <<= 8;
	    if (i < bytes || i - bytes >= left )
566 567 568 569
		data += *((uchar *)addr + i);
	    else
		data += *src++;
	}
W
wdenk 已提交
570

571 572 573 574 575 576 577 578 579 580 581 582 583
	/* write one word to the flash */
	switch (info->flash_id & FLASH_VENDMASK) {
	case FLASH_MAN_AMD:
		res = write_word_amd(info, (FPWV *)addr, data);
		break;
	case FLASH_MAN_INTEL:
		res = write_word_intel(info, (FPWV *)addr, data);
		break;
	default:
		/* unknown flash type, error! */
		printf ("missing or unknown FLASH type\n");
		res = 1;	/* not really a timeout, but gives error */
		break;
W
wdenk 已提交
584
	}
585
    }
W
wdenk 已提交
586

587
    return (res);
W
wdenk 已提交
588 589 590 591 592 593 594 595 596 597 598 599 600
}

/**
 * write_buf: - Copy memory to flash.
 *
 * @param info:
 * @param src:	source of copy transaction
 * @param addr:	where to copy to
 * @param cnt: 	number of bytes to copy
 *
 * @return	error code
 */

601
int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt)
W
wdenk 已提交
602 603 604 605 606 607 608 609 610 611 612 613 614
{
	ulong cp, wp;
	FPW data;
	int l;
	int i, rc;

	wp = (addr & ~1);	/* get lower word aligned address */

	/*
	 * handle unaligned start bytes
	 */
	if ((l = addr - wp) != 0) {
		data = 0;
615 616
		for (i=0, cp=wp; i<l; ++i, ++cp) {
			data = (data >> 8) | (*(uchar *)cp << 8);
W
wdenk 已提交
617
		}
618
		for (; i<2 && cnt>0; ++i) {
W
wdenk 已提交
619 620 621 622
			data = (data >> 8) | (*src++ << 8);
			--cnt;
			++cp;
		}
623 624
		for (; cnt==0 && i<2; ++i, ++cp) {
			data = (data >> 8) | (*(uchar *)cp << 8);
W
wdenk 已提交
625 626
		}

627
		if ((rc = write_word(info, wp, data)) != 0) {
W
wdenk 已提交
628 629 630 631 632 633 634 635 636 637
			return (rc);
		}
		wp += 2;
	}

	/*
	 * handle word aligned part
	 */
	while (cnt >= 2) {
		/* data = *((vushort*)src); */
638 639
		data = *((FPW*)src);
		if ((rc = write_word(info, wp, data)) != 0) {
W
wdenk 已提交
640 641
			return (rc);
		}
642 643 644
		src += sizeof(FPW);
		wp  += sizeof(FPW);
		cnt -= sizeof(FPW);
W
wdenk 已提交
645 646
	}

647
	if (cnt == 0) return ERR_OK;
W
wdenk 已提交
648 649 650 651 652

	/*
	 * handle unaligned tail bytes
	 */
	data = 0;
653
	for (i=0, cp=wp; i<2 && cnt>0; ++i, ++cp) {
W
wdenk 已提交
654 655 656
		data = (data >> 8) | (*src++ << 8);
		--cnt;
	}
657 658
	for (; i<2; ++i, ++cp) {
		data = (data >> 8) | (*(uchar *)cp << 8);
W
wdenk 已提交
659 660
	}

661
	return write_word(info, wp, data);
W
wdenk 已提交
662 663 664 665 666 667 668 669 670 671 672 673 674
}


/*-----------------------------------------------------------------------
 * Write a word to Flash for AMD FLASH
 * A word is 16 or 32 bits, whichever the bus width of the flash bank
 * (not an individual chip) is.
 *
 * returns:
 * 0 - OK
 * 1 - write timeout
 * 2 - Flash not erased
 */
675
static int write_word_amd (flash_info_t *info, FPWV *dest, FPW data)
W
wdenk 已提交
676
{
677 678 679 680
    ulong start;
    int flag;
    int res = 0;	/* result, assume success	*/
    FPWV *base;		/* first address in flash bank	*/
W
wdenk 已提交
681

682 683 684 685
    /* Check if Flash is (sufficiently) erased */
    if ((*dest & data) != data) {
	return (2);
    }
W
wdenk 已提交
686 687


688 689 690
    base = (FPWV *)(info->start[0]);
    /* Disable interrupts which might cause a timeout here */
    flag = disable_interrupts();
W
wdenk 已提交
691

692 693 694
    base[0x0555] = (FPW)0x00AA00AA;	/* unlock */
    base[0x02AA] = (FPW)0x00550055;	/* unlock */
    base[0x0555] = (FPW)0x00A000A0;	/* selects program mode */
W
wdenk 已提交
695

696
    *dest = data;		/* start programming the data	*/
W
wdenk 已提交
697

698 699 700
    /* re-enable interrupts if necessary */
    if (flag)
	enable_interrupts();
W
wdenk 已提交
701

702 703 704 705 706 707 708
    start = get_timer (0);

    /* data polling for D7 */
    while (res == 0 && (*dest & (FPW)0x00800080) != (data & (FPW)0x00800080)) {
	if (get_timer(start) > CFG_FLASH_WRITE_TOUT) {
	    *dest = (FPW)0x00F000F0;	/* reset bank */
	    res = 1;
W
wdenk 已提交
709
	}
710
    }
W
wdenk 已提交
711

712
    return (res);
W
wdenk 已提交
713 714 715 716 717 718 719 720 721 722 723 724
}

/*-----------------------------------------------------------------------
 * Write a word to Flash for Intel FLASH
 * A word is 16 or 32 bits, whichever the bus width of the flash bank
 * (not an individual chip) is.
 *
 * returns:
 * 0 - OK
 * 1 - write timeout
 * 2 - Flash not erased
 */
725
static int write_word_intel (flash_info_t *info, FPWV *dest, FPW data)
W
wdenk 已提交
726
{
727 728 729
    ulong start;
    int flag;
    int res = 0;	/* result, assume success	*/
W
wdenk 已提交
730

731 732 733 734
    /* Check if Flash is (sufficiently) erased */
    if ((*dest & data) != data) {
	return (2);
    }
W
wdenk 已提交
735

736 737
    /* Disable interrupts which might cause a timeout here */
    flag = disable_interrupts();
W
wdenk 已提交
738

739 740 741
    *dest = (FPW)0x00500050;	/* clear status register	*/
    *dest = (FPW)0x00FF00FF;	/* make sure in read mode	*/
    *dest = (FPW)0x00400040;	/* program setup		*/
W
wdenk 已提交
742

743
    *dest = data;		/* start programming the data	*/
W
wdenk 已提交
744

745 746 747
    /* re-enable interrupts if necessary */
    if (flag)
	enable_interrupts();
W
wdenk 已提交
748

749
    start = get_timer (0);
W
wdenk 已提交
750

751 752 753 754
    while (res == 0 && (*dest & (FPW)0x00800080) != (FPW)0x00800080) {
	if (get_timer(start) > CFG_FLASH_WRITE_TOUT) {
	    *dest = (FPW)0x00B000B0;	/* Suspend program	*/
	    res = 1;
W
wdenk 已提交
755
	}
756
    }
W
wdenk 已提交
757

758 759
    if (res == 0 && (*dest & (FPW)0x00100010))
	res = 1;	/* write failed, time out error is close enough	*/
W
wdenk 已提交
760

761 762
    *dest = (FPW)0x00500050;	/* clear status register	*/
    *dest = (FPW)0x00FF00FF;	/* make sure in read mode	*/
W
wdenk 已提交
763

764
    return (res);
W
wdenk 已提交
765 766 767 768 769 770 771
}

#ifdef CFG_FLASH_PROTECTION
/*-----------------------------------------------------------------------
 */
int flash_real_protect (flash_info_t * info, long sector, int prot)
{
772 773
	int rcode = 0;		/* assume success */
	FPWV *addr;		/* address of sector */
W
wdenk 已提交
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
	FPW value;

	addr = (FPWV *) (info->start[sector]);

	switch (info->flash_id & FLASH_TYPEMASK) {
	case FLASH_28F800C3B:
	case FLASH_28F800C3T:
	case FLASH_28F160C3B:
	case FLASH_28F160C3T:
	case FLASH_28F320C3B:
	case FLASH_28F320C3T:
	case FLASH_28F640C3B:
	case FLASH_28F640C3T:
		flash_reset (info);		/* make sure in read mode */
		*addr = (FPW) 0x00600060L;	/* lock command setup */
		if (prot)
			*addr = (FPW) 0x00010001L;	/* lock sector */
		else
			*addr = (FPW) 0x00D000D0L;	/* unlock sector */
		flash_reset (info);		/* reset to read mode */

		/* now see if it really is locked/unlocked as requested */
		*addr = (FPW) 0x00900090;
		/* read sector protection at sector address, (A7 .. A0) = 0x02.
		 * D0 = 1 for each device if protected.
		 * If at least one device is protected the sector is marked
		 * protected, but return failure. Mixed protected and
		 * unprotected devices within a sector should never happen.
		 */
		value = addr[2] & (FPW) 0x00010001;
		if (value == 0)
			info->protect[sector] = 0;
		else if (value == (FPW) 0x00010001)
			info->protect[sector] = 1;
		else {
			/* error, mixed protected and unprotected */
			rcode = 1;
			info->protect[sector] = 1;
		}
		if (info->protect[sector] != prot)
814
			rcode = 1;	/* failed to protect/unprotect as requested */
W
wdenk 已提交
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829

		/* reload all protection bits from hardware for now */
		flash_sync_real_protect (info);
		break;

	case FLASH_AM640U:
	default:
		/* no hardware protect that we support */
		info->protect[sector] = prot;
		break;
	}

	return rcode;
}
#endif