|
|
|
@ -2,6 +2,10 @@ |
|
|
|
|
* (C) Copyright 2005 |
|
|
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
|
|
|
|
* |
|
|
|
|
* flash_real_protect() routine based on boards/alaska/flash.c |
|
|
|
|
* (C) Copyright 2001 |
|
|
|
|
* Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net |
|
|
|
|
* |
|
|
|
|
* See file CREDITS for list of people who contributed to this |
|
|
|
|
* project. |
|
|
|
|
* |
|
|
|
@ -23,29 +27,36 @@ |
|
|
|
|
|
|
|
|
|
#include <common.h> |
|
|
|
|
|
|
|
|
|
flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ |
|
|
|
|
/* Intel-compatible flash commands */ |
|
|
|
|
#define INTEL_ERASE 0x20 |
|
|
|
|
#define INTEL_PROGRAM 0x40 |
|
|
|
|
#define INTEL_CLEAR 0x50 |
|
|
|
|
#define INTEL_LOCKBIT 0x60 |
|
|
|
|
#define INTEL_PROTECT 0x01 |
|
|
|
|
#define INTEL_STATUS 0x70 |
|
|
|
|
#define INTEL_READID 0x90 |
|
|
|
|
#define INTEL_READID 0x90 |
|
|
|
|
#define INTEL_SUSPEND 0xB0 |
|
|
|
|
#define INTEL_CONFIRM 0xD0 |
|
|
|
|
#define INTEL_RESET 0xFF |
|
|
|
|
|
|
|
|
|
/* Intel-compatible flash status bits */ |
|
|
|
|
#define INTEL_FINISHED 0x80 |
|
|
|
|
#define INTEL_OK 0x80 |
|
|
|
|
|
|
|
|
|
/* 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 char FLASH_PORT_WIDTH; |
|
|
|
|
typedef volatile unsigned char FLASH_PORT_WIDTHV; |
|
|
|
|
#define FPW FLASH_PORT_WIDTH |
|
|
|
|
#define FPWV FLASH_PORT_WIDTHV |
|
|
|
|
#define FLASH_ID_MASK 0xFF |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#define FPW FLASH_PORT_WIDTH |
|
|
|
|
#define FPWV FLASH_PORT_WIDTHV |
|
|
|
|
|
|
|
|
|
#define ORMASK(size) ((-size) & OR_AM_MSK) |
|
|
|
|
|
|
|
|
|
#define FLASH_CYCLE1 0x0555 |
|
|
|
|
#define FLASH_CYCLE2 0x02aa |
|
|
|
|
|
|
|
|
|
flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ |
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------
|
|
|
|
|
* Functions |
|
|
|
|
*/ |
|
|
|
@ -53,6 +64,8 @@ static ulong flash_get_size(FPWV *addr, flash_info_t *info); |
|
|
|
|
static void flash_reset(flash_info_t *info); |
|
|
|
|
static flash_info_t *flash_get_info(ulong base); |
|
|
|
|
static int write_data (flash_info_t *info, FPWV *dest, FPW data); /* O2D */ |
|
|
|
|
static void flash_sync_real_protect (flash_info_t * info); |
|
|
|
|
static unsigned char intel_sector_protected (flash_info_t *info, ushort sector); |
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------
|
|
|
|
|
* flash_init() |
|
|
|
@ -79,6 +92,9 @@ unsigned long flash_init (void) |
|
|
|
|
flash_get_size((FPW *)CFG_FLASH_BASE, &flash_info[0]); |
|
|
|
|
size += flash_info[0].size; |
|
|
|
|
|
|
|
|
|
/* get the h/w and s/w protection status in sync */ |
|
|
|
|
flash_sync_real_protect(&flash_info[0]); |
|
|
|
|
|
|
|
|
|
#if CFG_MONITOR_BASE >= CFG_FLASH_BASE |
|
|
|
|
/* monitor protection ON by default */ |
|
|
|
|
flash_protect(FLAG_PROTECT_SET, |
|
|
|
@ -108,7 +124,7 @@ static void flash_reset(flash_info_t *info) |
|
|
|
|
|
|
|
|
|
/* Put FLASH back in read mode */ |
|
|
|
|
if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) |
|
|
|
|
*base = (FPW)0x00FF00FF; /* Intel Read Mode */ |
|
|
|
|
*base = (FPW) INTEL_RESET; /* Intel Read Mode */ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------
|
|
|
|
@ -204,7 +220,7 @@ ulong flash_get_size (FPWV *addr, flash_info_t *info) |
|
|
|
|
|
|
|
|
|
/* Write auto select command: read Manufacturer ID */ |
|
|
|
|
/* Write auto select command sequence and test FLASH answer */ |
|
|
|
|
addr[FLASH_CYCLE1] = (FPW)0x00900090; /* selects Intel or AMD */ |
|
|
|
|
addr[FLASH_CYCLE1] = (FPW) INTEL_READID; /* selects Intel or AMD */ |
|
|
|
|
|
|
|
|
|
/* The manufacturer codes are only 1 byte, so just use 1 byte.
|
|
|
|
|
* This works for any bus width and any FLASH device width. |
|
|
|
@ -298,9 +314,9 @@ int flash_erase (flash_info_t *info, int s_first, int s_last) |
|
|
|
|
flag = disable_interrupts(); |
|
|
|
|
|
|
|
|
|
addr = (FPWV *)(info->start[sect]); |
|
|
|
|
*addr = (FPW)0x00500050; /* clear status register */ |
|
|
|
|
*addr = (FPW)0x00200020; /* erase setup */ |
|
|
|
|
*addr = (FPW)0x00D000D0; /* erase confirm */ |
|
|
|
|
*addr = (FPW) INTEL_CLEAR; /* clear status register */ |
|
|
|
|
*addr = (FPW) INTEL_ERASE; /* erase setup */ |
|
|
|
|
*addr = (FPW) INTEL_CONFIRM; /* erase confirm */ |
|
|
|
|
|
|
|
|
|
/* re-enable interrupts if necessary */ |
|
|
|
|
if (flag) |
|
|
|
@ -311,10 +327,10 @@ int flash_erase (flash_info_t *info, int s_first, int s_last) |
|
|
|
|
/* wait at least 80us for Intel - let's wait 1 ms */ |
|
|
|
|
udelay (1000); |
|
|
|
|
|
|
|
|
|
while ((*addr & (FPW)0x00800080) != (FPW)0x00800080) { |
|
|
|
|
while ((*addr & (FPW) INTEL_FINISHED) != (FPW) INTEL_FINISHED) { |
|
|
|
|
if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { |
|
|
|
|
printf ("Timeout\n"); |
|
|
|
|
*addr = (FPW)0x00B000B0;/* suspend erase */ |
|
|
|
|
*addr = (FPW) INTEL_SUSPEND;/* suspend erase */ |
|
|
|
|
flash_reset(info); /* reset to read mode */ |
|
|
|
|
rcode = 1; /* failed */ |
|
|
|
|
break; |
|
|
|
@ -401,23 +417,172 @@ static int write_data (flash_info_t *info, FPWV *dest, FPW data) |
|
|
|
|
/* Disable interrupts which might cause a timeout here */ |
|
|
|
|
flag = disable_interrupts (); |
|
|
|
|
|
|
|
|
|
*addr = (FPW) 0x00400040; /* write setup */ |
|
|
|
|
*addr = (FPW) INTEL_PROGRAM; /* write setup */ |
|
|
|
|
*addr = data; |
|
|
|
|
|
|
|
|
|
/* arm simple, non interrupt dependent timer */ |
|
|
|
|
start = get_timer(0); |
|
|
|
|
|
|
|
|
|
/* wait while polling the status register */ |
|
|
|
|
while (((status = *addr) & (FPW) 0x00800080) != (FPW) 0x00800080) { |
|
|
|
|
while (((status = *addr) & (FPW) INTEL_FINISHED) != (FPW) INTEL_FINISHED) { |
|
|
|
|
if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { |
|
|
|
|
*addr = (FPW) 0x00FF00FF; /* restore read mode */ |
|
|
|
|
*addr = (FPW) INTEL_RESET; /* restore read mode */ |
|
|
|
|
return (1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
*addr = (FPW) 0x00FF00FF; /* restore read mode */ |
|
|
|
|
*addr = (FPW) INTEL_RESET; /* restore read mode */ |
|
|
|
|
if (flag) |
|
|
|
|
enable_interrupts(); |
|
|
|
|
|
|
|
|
|
return (0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------
|
|
|
|
|
* Set/Clear sector's lock bit, returns: |
|
|
|
|
* 0 - OK |
|
|
|
|
* 1 - Error (timeout, voltage problems, etc.) |
|
|
|
|
*/ |
|
|
|
|
int flash_real_protect (flash_info_t * info, long sector, int prot) |
|
|
|
|
{ |
|
|
|
|
ulong start; |
|
|
|
|
int i; |
|
|
|
|
int rc = 0; |
|
|
|
|
FPWV *addr = (FPWV *) (info->start[sector]); |
|
|
|
|
int flag = disable_interrupts (); |
|
|
|
|
|
|
|
|
|
*addr = INTEL_CLEAR; /* Clear status register */ |
|
|
|
|
if (prot) { /* Set sector lock bit */ |
|
|
|
|
*addr = INTEL_LOCKBIT; /* Sector lock bit */ |
|
|
|
|
*addr = INTEL_PROTECT; /* set */ |
|
|
|
|
} else { /* Clear sector lock bit */ |
|
|
|
|
*addr = INTEL_LOCKBIT; /* All sectors lock bits */ |
|
|
|
|
*addr = INTEL_CONFIRM; /* clear */ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
start = get_timer (0); |
|
|
|
|
|
|
|
|
|
while ((*addr & INTEL_FINISHED) != INTEL_FINISHED) { |
|
|
|
|
if (get_timer (start) > CFG_FLASH_UNLOCK_TOUT) { |
|
|
|
|
printf ("Flash lock bit operation timed out\n"); |
|
|
|
|
rc = 1; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (*addr != INTEL_OK) { |
|
|
|
|
printf ("Flash lock bit operation failed at %08X, CSR=%08X\n", |
|
|
|
|
(uint) addr, (uint) * addr); |
|
|
|
|
rc = 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!rc) |
|
|
|
|
info->protect[sector] = prot; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Clear lock bit command clears all sectors lock bits, so |
|
|
|
|
* we have to restore lock bits of protected sectors. |
|
|
|
|
*/ |
|
|
|
|
if (!prot) { |
|
|
|
|
for (i = 0; i < info->sector_count; i++) { |
|
|
|
|
if (info->protect[i]) { |
|
|
|
|
start = get_timer (0); |
|
|
|
|
addr = (FPWV *) (info->start[i]); |
|
|
|
|
*addr = INTEL_LOCKBIT; /* Sector lock bit */ |
|
|
|
|
*addr = INTEL_PROTECT; /* set */ |
|
|
|
|
while ((*addr & INTEL_FINISHED) != |
|
|
|
|
INTEL_FINISHED) { |
|
|
|
|
if (get_timer (start) > |
|
|
|
|
CFG_FLASH_UNLOCK_TOUT) { |
|
|
|
|
printf ("Flash lock bit operation timed out\n"); |
|
|
|
|
rc = 1; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (flag) |
|
|
|
|
enable_interrupts (); |
|
|
|
|
|
|
|
|
|
*addr = INTEL_RESET; /* Reset to read array mode */ |
|
|
|
|
|
|
|
|
|
return rc; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function gets the u-boot flash sector protection status |
|
|
|
|
* (flash_info_t.protect[]) in sync with the sector protection |
|
|
|
|
* status stored in hardware. |
|
|
|
|
*/ |
|
|
|
|
static void flash_sync_real_protect (flash_info_t * info) |
|
|
|
|
{ |
|
|
|
|
int i; |
|
|
|
|
switch (info->flash_id & FLASH_TYPEMASK) { |
|
|
|
|
case FLASH_28F128J3A: |
|
|
|
|
for (i = 0; i < info->sector_count; ++i) { |
|
|
|
|
info->protect[i] = intel_sector_protected(info, i); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
/* no h/w protect support */ |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* checks if "sector" in bank "info" is protected. Should work on intel |
|
|
|
|
* strata flash chips 28FxxxJ3x in 8-bit mode. |
|
|
|
|
* Returns 1 if sector is protected (or timed-out while trying to read |
|
|
|
|
* protection status), 0 if it is not. |
|
|
|
|
*/ |
|
|
|
|
static unsigned char intel_sector_protected (flash_info_t *info, ushort sector) |
|
|
|
|
{ |
|
|
|
|
FPWV *addr; |
|
|
|
|
FPWV *lock_conf_addr; |
|
|
|
|
ulong start; |
|
|
|
|
unsigned char ret; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* first, wait for the WSM to be finished. The rationale for |
|
|
|
|
* waiting for the WSM to become idle for at most |
|
|
|
|
* CFG_FLASH_ERASE_TOUT is as follows. The WSM can be busy |
|
|
|
|
* because of: (1) erase, (2) program or (3) lock bit |
|
|
|
|
* configuration. So we just wait for the longest timeout of |
|
|
|
|
* the (1)-(3), i.e. the erase timeout. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/* wait at least 35ns (W12) before issuing Read Status Register */ |
|
|
|
|
udelay(1); |
|
|
|
|
addr = (FPWV *) info->start[sector]; |
|
|
|
|
*addr = (FPW) INTEL_STATUS; |
|
|
|
|
|
|
|
|
|
start = get_timer (0); |
|
|
|
|
while ((*addr & (FPW) INTEL_FINISHED) != (FPW) INTEL_FINISHED) { |
|
|
|
|
if (get_timer (start) > CFG_FLASH_ERASE_TOUT) { |
|
|
|
|
*addr = (FPW) INTEL_RESET; /* restore read mode */ |
|
|
|
|
printf("WSM busy too long, can't get prot status\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* issue the Read Identifier Codes command */ |
|
|
|
|
*addr = (FPW) INTEL_READID; |
|
|
|
|
|
|
|
|
|
/* wait at least 35ns (W12) before reading */ |
|
|
|
|
udelay(1); |
|
|
|
|
|
|
|
|
|
/* Intel example code uses offset of 4 for 8-bit flash */ |
|
|
|
|
lock_conf_addr = (FPWV *) info->start[sector] + 4; |
|
|
|
|
ret = (*lock_conf_addr & (FPW) INTEL_PROTECT) ? 1 : 0; |
|
|
|
|
|
|
|
|
|
/* put flash back in read mode */ |
|
|
|
|
*addr = (FPW) INTEL_RESET; |
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|