* Copyright (C) 2006
* Tolunay Orkun <listmember@orkun.us>
*
- * 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
- *
+ * SPDX-License-Identifier: GPL-2.0+
*/
/* The DEBUG define must be before common to enable debugging */
#include <asm/processor.h>
#include <asm/io.h>
#include <asm/byteorder.h>
+#include <asm/unaligned.h>
#include <environment.h>
#include <mtd/cfi_flash.h>
+#include <watchdog.h>
/*
* This file implements a Common Flash Interface (CFI) driver for
flash_info_t *flash_get_info(ulong base)
{
int i;
- flash_info_t *info = NULL;
+ flash_info_t *info;
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
- info = & flash_info[i];
+ info = &flash_info[i];
if (info->size && info->start[0] <= base &&
base <= info->start[0] + info->size - 1)
- break;
+ return info;
}
- return info;
+ return NULL;
}
#endif
reset_timer();
#endif
start = get_timer (0);
+ WATCHDOG_RESET();
while (flash_is_busy (info, sector)) {
if (get_timer (start) > tout) {
printf ("Flash %s timeout at address %lx data %lx\n",
reset_timer();
#endif
start = get_timer(0);
+ WATCHDOG_RESET();
while (1) {
switch (info->portwidth) {
case FLASH_CFI_8BIT:
*/
static flash_sect_t find_sector (flash_info_t * info, ulong addr)
{
- static flash_sect_t saved_sector = 0; /* previously found sector */
- static flash_info_t *saved_info = 0; /* previously used flash bank */
+ static flash_sect_t saved_sector; /* previously found sector */
+ static flash_info_t *saved_info; /* previously used flash bank */
flash_sect_t sector = saved_sector;
if ((info != saved_info) || (sector >= info->sector_count))
void *src = cp;
void *dst = (void *)dest;
void *dst2 = dst;
- int flag = 0;
+ int flag = 1;
uint offset = 0;
unsigned int shift;
uchar write_cmd;
cnt = len >> shift;
- while ((cnt-- > 0) && (flag == 0)) {
+ while ((cnt-- > 0) && (flag == 1)) {
switch (info->portwidth) {
case FLASH_CFI_8BIT:
flag = ((flash_read8(dst2) & flash_read8(src)) ==
for (sect = s_first; sect <= s_last; sect++) {
+ if (ctrlc()) {
+ printf("\n");
+ return 1;
+ }
+
if (info->protect[sect] == 0) { /* not protected */
+#ifdef CONFIG_SYS_FLASH_CHECK_BLANK_BEFORE_ERASE
+ int k;
+ int size;
+ int erased;
+ u32 *flash;
+
+ /*
+ * Check if whole sector is erased
+ */
+ size = flash_sector_size(info, sect);
+ erased = 1;
+ flash = (u32 *)info->start[sect];
+ /* divide by 4 for longword access */
+ size = size >> 2;
+ for (k = 0; k < size; k++) {
+ if (flash_read32(flash++) != 0xffffffff) {
+ erased = 0;
+ break;
+ }
+ }
+ if (erased) {
+ if (flash_verbose)
+ putc(',');
+ continue;
+ }
+#endif
switch (info->vendor) {
case CFI_CMDSET_INTEL_PROG_REGIONS:
case CFI_CMDSET_INTEL_STANDARD:
AMD_CMD_ERASE_START);
flash_unlock_seq (info, sect);
flash_write_cmd (info, sect, 0,
- AMD_CMD_ERASE_SECTOR);
+ info->cmd_erase_sector);
break;
#ifdef CONFIG_FLASH_CFI_LEGACY
case CFI_CMDSET_AMD_LEGACY:
}
if (use_flash_status_poll(info)) {
- cfiword_t cword = (cfiword_t)0xffffffffffffffffULL;
+ cfiword_t cword;
void *dest;
+ cword.ll = 0xffffffffffffffffULL;
dest = flash_map(info, sect, 0);
st = flash_status_poll(info, &cword, dest,
info->erase_blk_tout, "erase");
printf(info->chipwidth == FLASH_CFI_16BIT ? "%04X" : "%02X",
info->device_id2);
}
+ if ((info->vendor == CFI_CMDSET_AMD_STANDARD) && (info->legacy_unlock))
+ printf("\n Advanced Sector Protection (PPB) enabled");
printf ("\n Erase timeout: %ld ms, write timeout: %ld ms\n",
info->erase_blk_tout,
info->write_tout);
src += i;
cnt -= i;
FLASH_SHOW_PROGRESS(scale, dots, digit, i);
+ /* Only check every once in a while */
+ if ((cnt & 0xFFFF) < buffered_size && ctrlc())
+ return ERR_ABORTED;
}
#else
while (cnt >= info->portwidth) {
wp += info->portwidth;
cnt -= info->portwidth;
FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth);
+ /* Only check every once in a while */
+ if ((cnt & 0xFFFF) < info->portwidth && ctrlc())
+ return ERR_ABORTED;
}
#endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */
return flash_write_cfiword (info, wp, cword);
}
+static inline int manufact_match(flash_info_t *info, u32 manu)
+{
+ return info->manufacturer_id == ((manu & FLASH_VENDMASK) >> 16);
+}
+
/*-----------------------------------------------------------------------
*/
#ifdef CONFIG_SYS_FLASH_PROTECTION
+static int cfi_protect_bugfix(flash_info_t *info, long sector, int prot)
+{
+ if (manufact_match(info, INTEL_MANUFACT)
+ && info->device_id == NUMONYX_256MBIT) {
+ /*
+ * see errata called
+ * "Numonyx Axcell P33/P30 Specification Update" :)
+ */
+ flash_write_cmd(info, sector, 0, FLASH_CMD_READ_ID);
+ if (!flash_isequal(info, sector, FLASH_OFFSET_PROTECT,
+ prot)) {
+ /*
+ * cmd must come before FLASH_CMD_PROTECT + 20us
+ * Disable interrupts which might cause a timeout here.
+ */
+ int flag = disable_interrupts();
+ unsigned short cmd;
+
+ if (prot)
+ cmd = FLASH_CMD_PROTECT_SET;
+ else
+ cmd = FLASH_CMD_PROTECT_CLEAR;
+ flash_write_cmd(info, sector, 0,
+ FLASH_CMD_PROTECT);
+ flash_write_cmd(info, sector, 0, cmd);
+ /* re-enable interrupts if necessary */
+ if (flag)
+ enable_interrupts();
+ }
+ return 1;
+ }
+ return 0;
+}
+
int flash_real_protect (flash_info_t * info, long sector, int prot)
{
int retcode = 0;
case CFI_CMDSET_INTEL_PROG_REGIONS:
case CFI_CMDSET_INTEL_STANDARD:
case CFI_CMDSET_INTEL_EXTENDED:
- /*
- * see errata called
- * "Numonyx Axcell P33/P30 Specification Update" :)
- */
- flash_write_cmd (info, sector, 0, FLASH_CMD_READ_ID);
- if (!flash_isequal (info, sector, FLASH_OFFSET_PROTECT,
- prot)) {
- /*
- * cmd must come before FLASH_CMD_PROTECT + 20us
- * Disable interrupts which might cause a timeout here.
- */
- int flag = disable_interrupts ();
- unsigned short cmd;
-
+ if (!cfi_protect_bugfix(info, sector, prot)) {
+ flash_write_cmd(info, sector, 0,
+ FLASH_CMD_CLEAR_STATUS);
+ flash_write_cmd(info, sector, 0,
+ FLASH_CMD_PROTECT);
if (prot)
- cmd = FLASH_CMD_PROTECT_SET;
+ flash_write_cmd(info, sector, 0,
+ FLASH_CMD_PROTECT_SET);
else
- cmd = FLASH_CMD_PROTECT_CLEAR;
+ flash_write_cmd(info, sector, 0,
+ FLASH_CMD_PROTECT_CLEAR);
- flash_write_cmd (info, sector, 0,
- FLASH_CMD_PROTECT);
- flash_write_cmd (info, sector, 0, cmd);
- /* re-enable interrupts if necessary */
- if (flag)
- enable_interrupts ();
}
break;
case CFI_CMDSET_AMD_EXTENDED:
case CFI_CMDSET_AMD_STANDARD:
/* U-Boot only checks the first byte */
- if (info->manufacturer_id == (uchar)ATM_MANUFACT) {
+ if (manufact_match(info, ATM_MANUFACT)) {
if (prot) {
flash_unlock_seq (info, 0);
flash_write_cmd (info, 0,
0, ATM_CMD_UNLOCK_SECT);
}
}
+ if (info->legacy_unlock) {
+ int flag = disable_interrupts();
+ int lock_flag;
+
+ flash_unlock_seq(info, 0);
+ flash_write_cmd(info, 0, info->addr_unlock1,
+ AMD_CMD_SET_PPB_ENTRY);
+ lock_flag = flash_isset(info, sector, 0, 0x01);
+ if (prot) {
+ if (lock_flag) {
+ flash_write_cmd(info, sector, 0,
+ AMD_CMD_PPB_LOCK_BC1);
+ flash_write_cmd(info, sector, 0,
+ AMD_CMD_PPB_LOCK_BC2);
+ }
+ debug("sector %ld %slocked\n", sector,
+ lock_flag ? "" : "already ");
+ } else {
+ if (!lock_flag) {
+ debug("unlock %ld\n", sector);
+ flash_write_cmd(info, 0, 0,
+ AMD_CMD_PPB_UNLOCK_BC1);
+ flash_write_cmd(info, 0, 0,
+ AMD_CMD_PPB_UNLOCK_BC2);
+ }
+ debug("sector %ld %sunlocked\n", sector,
+ !lock_flag ? "" : "already ");
+ }
+ if (flag)
+ enable_interrupts();
+
+ if (flash_status_check(info, sector,
+ info->erase_blk_tout,
+ prot ? "protect" : "unprotect"))
+ printf("status check error\n");
+
+ flash_write_cmd(info, 0, 0,
+ AMD_CMD_SET_PPB_EXIT_BC1);
+ flash_write_cmd(info, 0, 0,
+ AMD_CMD_SET_PPB_EXIT_BC2);
+ }
break;
#ifdef CONFIG_FLASH_CFI_LEGACY
case CFI_CMDSET_AMD_LEGACY:
u32 tmp;
for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) {
- tmp = qry->erase_region_info[i];
- qry->erase_region_info[i] = qry->erase_region_info[j];
- qry->erase_region_info[j] = tmp;
+ tmp = get_unaligned(&(qry->erase_region_info[i]));
+ put_unaligned(get_unaligned(&(qry->erase_region_info[j])),
+ &(qry->erase_region_info[i]));
+ put_unaligned(tmp, &(qry->erase_region_info[j]));
}
}
static int cmdset_amd_init(flash_info_t *info, struct cfi_qry *qry)
{
info->cmd_reset = AMD_CMD_RESET;
+ info->cmd_erase_sector = AMD_CMD_ERASE_SECTOR;
cmdset_amd_read_jedec_ids(info);
flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI);
+#ifdef CONFIG_SYS_FLASH_PROTECTION
+ if (info->ext_addr) {
+ /* read sector protect/unprotect scheme (at 0x49) */
+ if (flash_read_uchar(info, info->ext_addr + 9) == 0x8)
+ info->legacy_unlock = 1;
+ }
+#endif
+
return 0;
}
};
int i;
- for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
info->vendor = modes[i];
info->start[0] =
(ulong)map_physmem(base,
break;
else
unmap_physmem((void *)info->start[0],
- MAP_NOCACHE);
+ info->portwidth);
}
}
p[i] = flash_read_uchar(info, start + i);
}
-void __flash_cmd_reset(flash_info_t *info)
+static void __flash_cmd_reset(flash_info_t *info)
{
/*
* We do not yet know what kind of commandset to use, so we issue
/* Issue FLASH reset command */
flash_cmd_reset(info);
- for (cfi_offset=0;
- cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint);
+ for (cfi_offset = 0; cfi_offset < ARRAY_SIZE(flash_offset_cfi);
cfi_offset++) {
flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],
FLASH_CMD_CFI);
}
}
+static void flash_fixup_sst(flash_info_t *info, struct cfi_qry *qry)
+{
+ /*
+ * SST, for many recent nor parallel flashes, says they are
+ * CFI-conformant. This is not true, since qry struct.
+ * reports a std. AMD command set (0x0002), while SST allows to
+ * erase two different sector sizes for the same memory.
+ * 64KB sector (SST call it block) needs 0x30 to be erased.
+ * 4KB sector (SST call it sector) needs 0x50 to be erased.
+ * Since CFI query detect the 4KB number of sectors, users expects
+ * a sector granularity of 4KB, and it is here set.
+ */
+ if (info->device_id == 0x5D23 || /* SST39VF3201B */
+ info->device_id == 0x5C23) { /* SST39VF3202B */
+ /* set sector granularity to 4KB */
+ info->cmd_erase_sector=0x50;
+ }
+}
+
+static void flash_fixup_num(flash_info_t *info, struct cfi_qry *qry)
+{
+ /*
+ * The M29EW devices seem to report the CFI information wrong
+ * when it's in 8 bit mode.
+ * There's an app note from Numonyx on this issue.
+ * So adjust the buffer size for M29EW while operating in 8-bit mode
+ */
+ if (((qry->max_buf_write_size) > 0x8) &&
+ (info->device_id == 0x7E) &&
+ (info->device_id2 == 0x2201 ||
+ info->device_id2 == 0x2301 ||
+ info->device_id2 == 0x2801 ||
+ info->device_id2 == 0x4801)) {
+ debug("Adjusted buffer size on Numonyx flash"
+ " M29EW family in 8 bit mode\n");
+ qry->max_buf_write_size = 0x8;
+ }
+}
+
/*
* The following code cannot be run from FLASH!
*
info->start[0] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE);
if (flash_detect_cfi (info, &qry)) {
- info->vendor = le16_to_cpu(qry.p_id);
- info->ext_addr = le16_to_cpu(qry.p_adr);
+ info->vendor = le16_to_cpu(get_unaligned(&(qry.p_id)));
+ info->ext_addr = le16_to_cpu(get_unaligned(&(qry.p_adr)));
num_erase_regions = qry.num_erase_regions;
if (info->ext_addr) {
case 0x0020:
flash_fixup_stm(info, &qry);
break;
+ case 0x00bf: /* SST */
+ flash_fixup_sst(info, &qry);
+ break;
+ case 0x0089: /* Numonyx */
+ flash_fixup_num(info, &qry);
+ break;
}
debug ("manufacturer is %d\n", info->vendor);
break;
}
- tmp = le32_to_cpu(qry.erase_region_info[i]);
+ tmp = le32_to_cpu(get_unaligned(
+ &(qry.erase_region_info[i])));
debug("erase region %u: 0x%08lx\n", i, tmp);
erase_region_count = (tmp & 0xffff) + 1;
FLASH_OFFSET_PROTECT,
FLASH_STATUS_PROTECT);
break;
+ case CFI_CMDSET_AMD_EXTENDED:
+ case CFI_CMDSET_AMD_STANDARD:
+ if (!info->legacy_unlock) {
+ /* default: not protected */
+ info->protect[sect_cnt] = 0;
+ break;
+ }
+
+ /* Read protection (PPB) from sector */
+ flash_write_cmd(info, 0, 0,
+ info->cmd_reset);
+ flash_unlock_seq(info, 0);
+ flash_write_cmd(info, 0,
+ info->addr_unlock1,
+ FLASH_CMD_READ_ID);
+ info->protect[sect_cnt] =
+ flash_isset(
+ info, sect_cnt,
+ FLASH_OFFSET_PROTECT,
+ FLASH_STATUS_PROTECT);
+ break;
default:
/* default: not protected */
info->protect[sect_cnt] = 0;
#endif
#if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST)
- for (i = 0; i < (sizeof(apl) / sizeof(struct apl_s)); i++) {
+ for (i = 0; i < ARRAY_SIZE(apl); i++) {
debug("autoprotecting from %08lx to %08lx\n",
apl[i].start, apl[i].start + apl[i].size - 1);
flash_protect(FLAG_PROTECT_SET,
#endif /* CONFIG_SYS_FLASH_QUIET_TEST */
}
#ifdef CONFIG_SYS_FLASH_PROTECTION
- else if ((s != NULL) && (strcmp(s, "yes") == 0)) {
+ else if (strcmp(s, "yes") == 0) {
/*
* Only the U-Boot image and it's environment
* is protected, all other sectors are