159829cc1SJean-Christophe PLAGNIOL-VILLARD /* 259829cc1SJean-Christophe PLAGNIOL-VILLARD * (C) Copyright 2002-2004 359829cc1SJean-Christophe PLAGNIOL-VILLARD * Brad Kemp, Seranoa Networks, Brad.Kemp@seranoa.com 459829cc1SJean-Christophe PLAGNIOL-VILLARD * 559829cc1SJean-Christophe PLAGNIOL-VILLARD * Copyright (C) 2003 Arabella Software Ltd. 659829cc1SJean-Christophe PLAGNIOL-VILLARD * Yuli Barcohen <yuli@arabellasw.com> 759829cc1SJean-Christophe PLAGNIOL-VILLARD * 859829cc1SJean-Christophe PLAGNIOL-VILLARD * Copyright (C) 2004 959829cc1SJean-Christophe PLAGNIOL-VILLARD * Ed Okerson 1059829cc1SJean-Christophe PLAGNIOL-VILLARD * 1159829cc1SJean-Christophe PLAGNIOL-VILLARD * Copyright (C) 2006 1259829cc1SJean-Christophe PLAGNIOL-VILLARD * Tolunay Orkun <listmember@orkun.us> 1359829cc1SJean-Christophe PLAGNIOL-VILLARD * 141a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 1559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1659829cc1SJean-Christophe PLAGNIOL-VILLARD 1759829cc1SJean-Christophe PLAGNIOL-VILLARD /* The DEBUG define must be before common to enable debugging */ 1859829cc1SJean-Christophe PLAGNIOL-VILLARD /* #define DEBUG */ 1959829cc1SJean-Christophe PLAGNIOL-VILLARD 2059829cc1SJean-Christophe PLAGNIOL-VILLARD #include <common.h> 2159829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/processor.h> 2259829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h> 2359829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/byteorder.h> 24aedadf10SAndrew Gabbasov #include <asm/unaligned.h> 2559829cc1SJean-Christophe PLAGNIOL-VILLARD #include <environment.h> 26fa36ae79SStefan Roese #include <mtd/cfi_flash.h> 27a9f5fabaSJens Scharsig (BuS Elektronik) #include <watchdog.h> 2859829cc1SJean-Christophe PLAGNIOL-VILLARD 2959829cc1SJean-Christophe PLAGNIOL-VILLARD /* 307e5b9b47SHaavard Skinnemoen * This file implements a Common Flash Interface (CFI) driver for 317e5b9b47SHaavard Skinnemoen * U-Boot. 327e5b9b47SHaavard Skinnemoen * 337e5b9b47SHaavard Skinnemoen * The width of the port and the width of the chips are determined at 347e5b9b47SHaavard Skinnemoen * initialization. These widths are used to calculate the address for 357e5b9b47SHaavard Skinnemoen * access CFI data structures. 3659829cc1SJean-Christophe PLAGNIOL-VILLARD * 3759829cc1SJean-Christophe PLAGNIOL-VILLARD * References 3859829cc1SJean-Christophe PLAGNIOL-VILLARD * JEDEC Standard JESD68 - Common Flash Interface (CFI) 3959829cc1SJean-Christophe PLAGNIOL-VILLARD * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes 4059829cc1SJean-Christophe PLAGNIOL-VILLARD * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets 4159829cc1SJean-Christophe PLAGNIOL-VILLARD * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet 4259829cc1SJean-Christophe PLAGNIOL-VILLARD * AMD CFI Specification, Release 2.0 December 1, 2001 4359829cc1SJean-Christophe PLAGNIOL-VILLARD * AMD/Spansion Application Note: Migration from Single-byte to Three-byte 4459829cc1SJean-Christophe PLAGNIOL-VILLARD * Device IDs, Publication Number 25538 Revision A, November 8, 2001 4559829cc1SJean-Christophe PLAGNIOL-VILLARD * 466d0f6bcfSJean-Christophe PLAGNIOL-VILLARD * Define CONFIG_SYS_WRITE_SWAPPED_DATA, if you have to swap the Bytes between 4759829cc1SJean-Christophe PLAGNIOL-VILLARD * reading and writing ... (yes there is such a Hardware). 4859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 4959829cc1SJean-Christophe PLAGNIOL-VILLARD 5059829cc1SJean-Christophe PLAGNIOL-VILLARD static uint flash_offset_cfi[2] = { FLASH_OFFSET_CFI, FLASH_OFFSET_CFI_ALT }; 514ffeab2cSMike Frysinger #ifdef CONFIG_FLASH_CFI_MTD 526ea808efSPiotr Ziecik static uint flash_verbose = 1; 534ffeab2cSMike Frysinger #else 544ffeab2cSMike Frysinger #define flash_verbose 1 554ffeab2cSMike Frysinger #endif 5659829cc1SJean-Christophe PLAGNIOL-VILLARD 572a112b23SWolfgang Denk flash_info_t flash_info[CFI_MAX_FLASH_BANKS]; /* FLASH chips info */ 582a112b23SWolfgang Denk 5959829cc1SJean-Christophe PLAGNIOL-VILLARD /* 6059829cc1SJean-Christophe PLAGNIOL-VILLARD * Check if chip width is defined. If not, start detecting with 8bit. 6159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 626d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifndef CONFIG_SYS_FLASH_CFI_WIDTH 636d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #define CONFIG_SYS_FLASH_CFI_WIDTH FLASH_CFI_8BIT 6459829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 6559829cc1SJean-Christophe PLAGNIOL-VILLARD 66*00dcb07cSJeroen Hofstee #ifdef CONFIG_CFI_FLASH_USE_WEAK_ACCESSORS 67*00dcb07cSJeroen Hofstee #define __maybe_weak __weak 68*00dcb07cSJeroen Hofstee #else 69*00dcb07cSJeroen Hofstee #define __maybe_weak static 70*00dcb07cSJeroen Hofstee #endif 71*00dcb07cSJeroen Hofstee 726f726f95SStefan Roese /* 736f726f95SStefan Roese * 0xffff is an undefined value for the configuration register. When 746f726f95SStefan Roese * this value is returned, the configuration register shall not be 756f726f95SStefan Roese * written at all (default mode). 766f726f95SStefan Roese */ 776f726f95SStefan Roese static u16 cfi_flash_config_reg(int i) 786f726f95SStefan Roese { 796f726f95SStefan Roese #ifdef CONFIG_SYS_CFI_FLASH_CONFIG_REGS 806f726f95SStefan Roese return ((u16 [])CONFIG_SYS_CFI_FLASH_CONFIG_REGS)[i]; 816f726f95SStefan Roese #else 826f726f95SStefan Roese return 0xffff; 836f726f95SStefan Roese #endif 846f726f95SStefan Roese } 856f726f95SStefan Roese 86ca5def3fSStefan Roese #if defined(CONFIG_SYS_MAX_FLASH_BANKS_DETECT) 87ca5def3fSStefan Roese int cfi_flash_num_flash_banks = CONFIG_SYS_MAX_FLASH_BANKS_DETECT; 88ca5def3fSStefan Roese #endif 89ca5def3fSStefan Roese 90*00dcb07cSJeroen Hofstee __weak phys_addr_t cfi_flash_bank_addr(int i) 91b00e19ccSStefan Roese { 92b00e19ccSStefan Roese return ((phys_addr_t [])CONFIG_SYS_FLASH_BANKS_LIST)[i]; 93b00e19ccSStefan Roese } 94b00e19ccSStefan Roese 95*00dcb07cSJeroen Hofstee __weak unsigned long cfi_flash_bank_size(int i) 96ec50a8e3SIlya Yanok { 97ec50a8e3SIlya Yanok #ifdef CONFIG_SYS_FLASH_BANKS_SIZES 98ec50a8e3SIlya Yanok return ((unsigned long [])CONFIG_SYS_FLASH_BANKS_SIZES)[i]; 99ec50a8e3SIlya Yanok #else 100ec50a8e3SIlya Yanok return 0; 101ec50a8e3SIlya Yanok #endif 102ec50a8e3SIlya Yanok } 103ec50a8e3SIlya Yanok 104*00dcb07cSJeroen Hofstee __maybe_weak void flash_write8(u8 value, void *addr) 105cdbaefb5SHaavard Skinnemoen { 106cdbaefb5SHaavard Skinnemoen __raw_writeb(value, addr); 107cdbaefb5SHaavard Skinnemoen } 108cdbaefb5SHaavard Skinnemoen 109*00dcb07cSJeroen Hofstee __maybe_weak void flash_write16(u16 value, void *addr) 110cdbaefb5SHaavard Skinnemoen { 111cdbaefb5SHaavard Skinnemoen __raw_writew(value, addr); 112cdbaefb5SHaavard Skinnemoen } 113cdbaefb5SHaavard Skinnemoen 114*00dcb07cSJeroen Hofstee __maybe_weak void flash_write32(u32 value, void *addr) 115cdbaefb5SHaavard Skinnemoen { 116cdbaefb5SHaavard Skinnemoen __raw_writel(value, addr); 117cdbaefb5SHaavard Skinnemoen } 118cdbaefb5SHaavard Skinnemoen 119*00dcb07cSJeroen Hofstee __maybe_weak void flash_write64(u64 value, void *addr) 120cdbaefb5SHaavard Skinnemoen { 121cdbaefb5SHaavard Skinnemoen /* No architectures currently implement __raw_writeq() */ 122cdbaefb5SHaavard Skinnemoen *(volatile u64 *)addr = value; 123cdbaefb5SHaavard Skinnemoen } 124cdbaefb5SHaavard Skinnemoen 125*00dcb07cSJeroen Hofstee __maybe_weak u8 flash_read8(void *addr) 126cdbaefb5SHaavard Skinnemoen { 127cdbaefb5SHaavard Skinnemoen return __raw_readb(addr); 128cdbaefb5SHaavard Skinnemoen } 129cdbaefb5SHaavard Skinnemoen 130*00dcb07cSJeroen Hofstee __maybe_weak u16 flash_read16(void *addr) 131cdbaefb5SHaavard Skinnemoen { 132cdbaefb5SHaavard Skinnemoen return __raw_readw(addr); 133cdbaefb5SHaavard Skinnemoen } 134cdbaefb5SHaavard Skinnemoen 135*00dcb07cSJeroen Hofstee __maybe_weak u32 flash_read32(void *addr) 136cdbaefb5SHaavard Skinnemoen { 137cdbaefb5SHaavard Skinnemoen return __raw_readl(addr); 138cdbaefb5SHaavard Skinnemoen } 139cdbaefb5SHaavard Skinnemoen 140*00dcb07cSJeroen Hofstee __maybe_weak u64 flash_read64(void *addr) 141cdbaefb5SHaavard Skinnemoen { 142cdbaefb5SHaavard Skinnemoen /* No architectures currently implement __raw_readq() */ 143cdbaefb5SHaavard Skinnemoen return *(volatile u64 *)addr; 144cdbaefb5SHaavard Skinnemoen } 145cdbaefb5SHaavard Skinnemoen 146be60a902SHaavard Skinnemoen /*----------------------------------------------------------------------- 147be60a902SHaavard Skinnemoen */ 1486d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if defined(CONFIG_ENV_IS_IN_FLASH) || defined(CONFIG_ENV_ADDR_REDUND) || (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE) 1494f975678SHeiko Schocher flash_info_t *flash_get_info(ulong base) 150be60a902SHaavard Skinnemoen { 151be60a902SHaavard Skinnemoen int i; 15224c185cfSMasahiro Yamada flash_info_t *info; 153be60a902SHaavard Skinnemoen 1546d0f6bcfSJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { 155be60a902SHaavard Skinnemoen info = &flash_info[i]; 156be60a902SHaavard Skinnemoen if (info->size && info->start[0] <= base && 157be60a902SHaavard Skinnemoen base <= info->start[0] + info->size - 1) 15824c185cfSMasahiro Yamada return info; 159be60a902SHaavard Skinnemoen } 160be60a902SHaavard Skinnemoen 16124c185cfSMasahiro Yamada return NULL; 162be60a902SHaavard Skinnemoen } 16359829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 16459829cc1SJean-Christophe PLAGNIOL-VILLARD 16512d30aa7SHaavard Skinnemoen unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect) 16612d30aa7SHaavard Skinnemoen { 16712d30aa7SHaavard Skinnemoen if (sect != (info->sector_count - 1)) 16812d30aa7SHaavard Skinnemoen return info->start[sect + 1] - info->start[sect]; 16912d30aa7SHaavard Skinnemoen else 17012d30aa7SHaavard Skinnemoen return info->start[0] + info->size - info->start[sect]; 17112d30aa7SHaavard Skinnemoen } 17212d30aa7SHaavard Skinnemoen 17359829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 17459829cc1SJean-Christophe PLAGNIOL-VILLARD * create an address based on the offset and the port width 17559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 17612d30aa7SHaavard Skinnemoen static inline void * 17712d30aa7SHaavard Skinnemoen flash_map (flash_info_t * info, flash_sect_t sect, uint offset) 17859829cc1SJean-Christophe PLAGNIOL-VILLARD { 179e303be2dSStefan Roese unsigned int byte_offset = offset * info->portwidth; 18012d30aa7SHaavard Skinnemoen 181e303be2dSStefan Roese return (void *)(info->start[sect] + byte_offset); 18212d30aa7SHaavard Skinnemoen } 18312d30aa7SHaavard Skinnemoen 18412d30aa7SHaavard Skinnemoen static inline void flash_unmap(flash_info_t *info, flash_sect_t sect, 18512d30aa7SHaavard Skinnemoen unsigned int offset, void *addr) 18612d30aa7SHaavard Skinnemoen { 18759829cc1SJean-Christophe PLAGNIOL-VILLARD } 18859829cc1SJean-Christophe PLAGNIOL-VILLARD 189be60a902SHaavard Skinnemoen /*----------------------------------------------------------------------- 190be60a902SHaavard Skinnemoen * make a proper sized command based on the port and chip widths 191be60a902SHaavard Skinnemoen */ 1927288f972SSebastian Siewior static void flash_make_cmd(flash_info_t *info, u32 cmd, void *cmdbuf) 193be60a902SHaavard Skinnemoen { 194be60a902SHaavard Skinnemoen int i; 19593c56f21SVasiliy Leoenenko int cword_offset; 19693c56f21SVasiliy Leoenenko int cp_offset; 1976d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA) 198340ccb26SSebastian Siewior u32 cmd_le = cpu_to_le32(cmd); 199340ccb26SSebastian Siewior #endif 20093c56f21SVasiliy Leoenenko uchar val; 201be60a902SHaavard Skinnemoen uchar *cp = (uchar *) cmdbuf; 202be60a902SHaavard Skinnemoen 20393c56f21SVasiliy Leoenenko for (i = info->portwidth; i > 0; i--){ 20493c56f21SVasiliy Leoenenko cword_offset = (info->portwidth-i)%info->chipwidth; 2056d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA) 20693c56f21SVasiliy Leoenenko cp_offset = info->portwidth - i; 207340ccb26SSebastian Siewior val = *((uchar*)&cmd_le + cword_offset); 208be60a902SHaavard Skinnemoen #else 20993c56f21SVasiliy Leoenenko cp_offset = i - 1; 2107288f972SSebastian Siewior val = *((uchar*)&cmd + sizeof(u32) - cword_offset - 1); 211be60a902SHaavard Skinnemoen #endif 2127288f972SSebastian Siewior cp[cp_offset] = (cword_offset >= sizeof(u32)) ? 0x00 : val; 21393c56f21SVasiliy Leoenenko } 214be60a902SHaavard Skinnemoen } 215be60a902SHaavard Skinnemoen 21659829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 21759829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 21859829cc1SJean-Christophe PLAGNIOL-VILLARD * Debug support 21959829cc1SJean-Christophe PLAGNIOL-VILLARD */ 2203055793bSHaavard Skinnemoen static void print_longlong (char *str, unsigned long long data) 22159829cc1SJean-Christophe PLAGNIOL-VILLARD { 22259829cc1SJean-Christophe PLAGNIOL-VILLARD int i; 22359829cc1SJean-Christophe PLAGNIOL-VILLARD char *cp; 22459829cc1SJean-Christophe PLAGNIOL-VILLARD 225657f2062SWolfgang Denk cp = (char *) &data; 22659829cc1SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < 8; i++) 22759829cc1SJean-Christophe PLAGNIOL-VILLARD sprintf (&str[i * 2], "%2.2x", *cp++); 22859829cc1SJean-Christophe PLAGNIOL-VILLARD } 229be60a902SHaavard Skinnemoen 230e23741f4SHaavard Skinnemoen static void flash_printqry (struct cfi_qry *qry) 23159829cc1SJean-Christophe PLAGNIOL-VILLARD { 232e23741f4SHaavard Skinnemoen u8 *p = (u8 *)qry; 23359829cc1SJean-Christophe PLAGNIOL-VILLARD int x, y; 23459829cc1SJean-Christophe PLAGNIOL-VILLARD 235e23741f4SHaavard Skinnemoen for (x = 0; x < sizeof(struct cfi_qry); x += 16) { 236e23741f4SHaavard Skinnemoen debug("%02x : ", x); 237e23741f4SHaavard Skinnemoen for (y = 0; y < 16; y++) 238e23741f4SHaavard Skinnemoen debug("%2.2x ", p[x + y]); 23959829cc1SJean-Christophe PLAGNIOL-VILLARD debug(" "); 24059829cc1SJean-Christophe PLAGNIOL-VILLARD for (y = 0; y < 16; y++) { 241e23741f4SHaavard Skinnemoen unsigned char c = p[x + y]; 242e23741f4SHaavard Skinnemoen if (c >= 0x20 && c <= 0x7e) 243cdbaefb5SHaavard Skinnemoen debug("%c", c); 244e23741f4SHaavard Skinnemoen else 24559829cc1SJean-Christophe PLAGNIOL-VILLARD debug("."); 24659829cc1SJean-Christophe PLAGNIOL-VILLARD } 24759829cc1SJean-Christophe PLAGNIOL-VILLARD debug("\n"); 24859829cc1SJean-Christophe PLAGNIOL-VILLARD } 24959829cc1SJean-Christophe PLAGNIOL-VILLARD } 25059829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 25159829cc1SJean-Christophe PLAGNIOL-VILLARD 25259829cc1SJean-Christophe PLAGNIOL-VILLARD 25359829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 25459829cc1SJean-Christophe PLAGNIOL-VILLARD * read a character at a port width address 25559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 2563055793bSHaavard Skinnemoen static inline uchar flash_read_uchar (flash_info_t * info, uint offset) 25759829cc1SJean-Christophe PLAGNIOL-VILLARD { 25859829cc1SJean-Christophe PLAGNIOL-VILLARD uchar *cp; 25912d30aa7SHaavard Skinnemoen uchar retval; 26059829cc1SJean-Christophe PLAGNIOL-VILLARD 26112d30aa7SHaavard Skinnemoen cp = flash_map (info, 0, offset); 2626d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA) 26312d30aa7SHaavard Skinnemoen retval = flash_read8(cp); 26459829cc1SJean-Christophe PLAGNIOL-VILLARD #else 26512d30aa7SHaavard Skinnemoen retval = flash_read8(cp + info->portwidth - 1); 26659829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 26712d30aa7SHaavard Skinnemoen flash_unmap (info, 0, offset, cp); 26812d30aa7SHaavard Skinnemoen return retval; 26959829cc1SJean-Christophe PLAGNIOL-VILLARD } 27059829cc1SJean-Christophe PLAGNIOL-VILLARD 27159829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 27290447ecbSTor Krill * read a word at a port width address, assume 16bit bus 27390447ecbSTor Krill */ 27490447ecbSTor Krill static inline ushort flash_read_word (flash_info_t * info, uint offset) 27590447ecbSTor Krill { 27690447ecbSTor Krill ushort *addr, retval; 27790447ecbSTor Krill 27890447ecbSTor Krill addr = flash_map (info, 0, offset); 27990447ecbSTor Krill retval = flash_read16 (addr); 28090447ecbSTor Krill flash_unmap (info, 0, offset, addr); 28190447ecbSTor Krill return retval; 28290447ecbSTor Krill } 28390447ecbSTor Krill 28490447ecbSTor Krill 28590447ecbSTor Krill /*----------------------------------------------------------------------- 28659829cc1SJean-Christophe PLAGNIOL-VILLARD * read a long word by picking the least significant byte of each maximum 28759829cc1SJean-Christophe PLAGNIOL-VILLARD * port size word. Swap for ppc format. 28859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 2893055793bSHaavard Skinnemoen static ulong flash_read_long (flash_info_t * info, flash_sect_t sect, 2903055793bSHaavard Skinnemoen uint offset) 29159829cc1SJean-Christophe PLAGNIOL-VILLARD { 29259829cc1SJean-Christophe PLAGNIOL-VILLARD uchar *addr; 29359829cc1SJean-Christophe PLAGNIOL-VILLARD ulong retval; 29459829cc1SJean-Christophe PLAGNIOL-VILLARD 29559829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 29659829cc1SJean-Christophe PLAGNIOL-VILLARD int x; 29759829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 29812d30aa7SHaavard Skinnemoen addr = flash_map (info, sect, offset); 29959829cc1SJean-Christophe PLAGNIOL-VILLARD 30059829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 30159829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("long addr is at %p info->portwidth = %d\n", addr, 30259829cc1SJean-Christophe PLAGNIOL-VILLARD info->portwidth); 30359829cc1SJean-Christophe PLAGNIOL-VILLARD for (x = 0; x < 4 * info->portwidth; x++) { 30412d30aa7SHaavard Skinnemoen debug ("addr[%x] = 0x%x\n", x, flash_read8(addr + x)); 30559829cc1SJean-Christophe PLAGNIOL-VILLARD } 30659829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 3076d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA) 30812d30aa7SHaavard Skinnemoen retval = ((flash_read8(addr) << 16) | 30912d30aa7SHaavard Skinnemoen (flash_read8(addr + info->portwidth) << 24) | 31012d30aa7SHaavard Skinnemoen (flash_read8(addr + 2 * info->portwidth)) | 31112d30aa7SHaavard Skinnemoen (flash_read8(addr + 3 * info->portwidth) << 8)); 31259829cc1SJean-Christophe PLAGNIOL-VILLARD #else 31312d30aa7SHaavard Skinnemoen retval = ((flash_read8(addr + 2 * info->portwidth - 1) << 24) | 31412d30aa7SHaavard Skinnemoen (flash_read8(addr + info->portwidth - 1) << 16) | 31512d30aa7SHaavard Skinnemoen (flash_read8(addr + 4 * info->portwidth - 1) << 8) | 31612d30aa7SHaavard Skinnemoen (flash_read8(addr + 3 * info->portwidth - 1))); 31759829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 31812d30aa7SHaavard Skinnemoen flash_unmap(info, sect, offset, addr); 31912d30aa7SHaavard Skinnemoen 32059829cc1SJean-Christophe PLAGNIOL-VILLARD return retval; 32159829cc1SJean-Christophe PLAGNIOL-VILLARD } 32259829cc1SJean-Christophe PLAGNIOL-VILLARD 323be60a902SHaavard Skinnemoen /* 324be60a902SHaavard Skinnemoen * Write a proper sized command to the correct address 32581b20cccSMichael Schwingen */ 326fa36ae79SStefan Roese void flash_write_cmd (flash_info_t * info, flash_sect_t sect, 3277288f972SSebastian Siewior uint offset, u32 cmd) 32881b20cccSMichael Schwingen { 3297e5b9b47SHaavard Skinnemoen 330cdbaefb5SHaavard Skinnemoen void *addr; 331be60a902SHaavard Skinnemoen cfiword_t cword; 33281b20cccSMichael Schwingen 33312d30aa7SHaavard Skinnemoen addr = flash_map (info, sect, offset); 334be60a902SHaavard Skinnemoen flash_make_cmd (info, cmd, &cword); 335be60a902SHaavard Skinnemoen switch (info->portwidth) { 336be60a902SHaavard Skinnemoen case FLASH_CFI_8BIT: 337cdbaefb5SHaavard Skinnemoen debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr, cmd, 338be60a902SHaavard Skinnemoen cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); 339cdbaefb5SHaavard Skinnemoen flash_write8(cword.c, addr); 340be60a902SHaavard Skinnemoen break; 341be60a902SHaavard Skinnemoen case FLASH_CFI_16BIT: 342cdbaefb5SHaavard Skinnemoen debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr, 343be60a902SHaavard Skinnemoen cmd, cword.w, 344be60a902SHaavard Skinnemoen info->chipwidth << CFI_FLASH_SHIFT_WIDTH); 345cdbaefb5SHaavard Skinnemoen flash_write16(cword.w, addr); 346be60a902SHaavard Skinnemoen break; 347be60a902SHaavard Skinnemoen case FLASH_CFI_32BIT: 348cdbaefb5SHaavard Skinnemoen debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit\n", addr, 349be60a902SHaavard Skinnemoen cmd, cword.l, 350be60a902SHaavard Skinnemoen info->chipwidth << CFI_FLASH_SHIFT_WIDTH); 351cdbaefb5SHaavard Skinnemoen flash_write32(cword.l, addr); 352be60a902SHaavard Skinnemoen break; 353be60a902SHaavard Skinnemoen case FLASH_CFI_64BIT: 354be60a902SHaavard Skinnemoen #ifdef DEBUG 355be60a902SHaavard Skinnemoen { 356be60a902SHaavard Skinnemoen char str[20]; 357be60a902SHaavard Skinnemoen 358be60a902SHaavard Skinnemoen print_longlong (str, cword.ll); 359be60a902SHaavard Skinnemoen 360be60a902SHaavard Skinnemoen debug ("fwrite addr %p cmd %x %s 64 bit x %d bit\n", 361cdbaefb5SHaavard Skinnemoen addr, cmd, str, 362be60a902SHaavard Skinnemoen info->chipwidth << CFI_FLASH_SHIFT_WIDTH); 36381b20cccSMichael Schwingen } 364be60a902SHaavard Skinnemoen #endif 365cdbaefb5SHaavard Skinnemoen flash_write64(cword.ll, addr); 36681b20cccSMichael Schwingen break; 36781b20cccSMichael Schwingen } 368be60a902SHaavard Skinnemoen 369be60a902SHaavard Skinnemoen /* Ensure all the instructions are fully finished */ 370be60a902SHaavard Skinnemoen sync(); 37112d30aa7SHaavard Skinnemoen 37212d30aa7SHaavard Skinnemoen flash_unmap(info, sect, offset, addr); 37381b20cccSMichael Schwingen } 3747e5b9b47SHaavard Skinnemoen 375be60a902SHaavard Skinnemoen static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect) 376be60a902SHaavard Skinnemoen { 377be60a902SHaavard Skinnemoen flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_UNLOCK_START); 378be60a902SHaavard Skinnemoen flash_write_cmd (info, sect, info->addr_unlock2, AMD_CMD_UNLOCK_ACK); 379be60a902SHaavard Skinnemoen } 380be60a902SHaavard Skinnemoen 381be60a902SHaavard Skinnemoen /*----------------------------------------------------------------------- 382be60a902SHaavard Skinnemoen */ 383be60a902SHaavard Skinnemoen static int flash_isequal (flash_info_t * info, flash_sect_t sect, 384be60a902SHaavard Skinnemoen uint offset, uchar cmd) 385be60a902SHaavard Skinnemoen { 386cdbaefb5SHaavard Skinnemoen void *addr; 387be60a902SHaavard Skinnemoen cfiword_t cword; 388be60a902SHaavard Skinnemoen int retval; 389be60a902SHaavard Skinnemoen 39012d30aa7SHaavard Skinnemoen addr = flash_map (info, sect, offset); 391be60a902SHaavard Skinnemoen flash_make_cmd (info, cmd, &cword); 392be60a902SHaavard Skinnemoen 393cdbaefb5SHaavard Skinnemoen debug ("is= cmd %x(%c) addr %p ", cmd, cmd, addr); 394be60a902SHaavard Skinnemoen switch (info->portwidth) { 395be60a902SHaavard Skinnemoen case FLASH_CFI_8BIT: 396cdbaefb5SHaavard Skinnemoen debug ("is= %x %x\n", flash_read8(addr), cword.c); 397cdbaefb5SHaavard Skinnemoen retval = (flash_read8(addr) == cword.c); 398be60a902SHaavard Skinnemoen break; 399be60a902SHaavard Skinnemoen case FLASH_CFI_16BIT: 400cdbaefb5SHaavard Skinnemoen debug ("is= %4.4x %4.4x\n", flash_read16(addr), cword.w); 401cdbaefb5SHaavard Skinnemoen retval = (flash_read16(addr) == cword.w); 402be60a902SHaavard Skinnemoen break; 403be60a902SHaavard Skinnemoen case FLASH_CFI_32BIT: 40452514699SAndrew Klossner debug ("is= %8.8x %8.8lx\n", flash_read32(addr), cword.l); 405cdbaefb5SHaavard Skinnemoen retval = (flash_read32(addr) == cword.l); 406be60a902SHaavard Skinnemoen break; 407be60a902SHaavard Skinnemoen case FLASH_CFI_64BIT: 408be60a902SHaavard Skinnemoen #ifdef DEBUG 409be60a902SHaavard Skinnemoen { 410be60a902SHaavard Skinnemoen char str1[20]; 411be60a902SHaavard Skinnemoen char str2[20]; 412be60a902SHaavard Skinnemoen 413cdbaefb5SHaavard Skinnemoen print_longlong (str1, flash_read64(addr)); 414be60a902SHaavard Skinnemoen print_longlong (str2, cword.ll); 415be60a902SHaavard Skinnemoen debug ("is= %s %s\n", str1, str2); 416be60a902SHaavard Skinnemoen } 417be60a902SHaavard Skinnemoen #endif 418cdbaefb5SHaavard Skinnemoen retval = (flash_read64(addr) == cword.ll); 419be60a902SHaavard Skinnemoen break; 420be60a902SHaavard Skinnemoen default: 421be60a902SHaavard Skinnemoen retval = 0; 422be60a902SHaavard Skinnemoen break; 423be60a902SHaavard Skinnemoen } 42412d30aa7SHaavard Skinnemoen flash_unmap(info, sect, offset, addr); 42512d30aa7SHaavard Skinnemoen 426be60a902SHaavard Skinnemoen return retval; 427be60a902SHaavard Skinnemoen } 428be60a902SHaavard Skinnemoen 429be60a902SHaavard Skinnemoen /*----------------------------------------------------------------------- 430be60a902SHaavard Skinnemoen */ 431be60a902SHaavard Skinnemoen static int flash_isset (flash_info_t * info, flash_sect_t sect, 432be60a902SHaavard Skinnemoen uint offset, uchar cmd) 433be60a902SHaavard Skinnemoen { 434cdbaefb5SHaavard Skinnemoen void *addr; 435be60a902SHaavard Skinnemoen cfiword_t cword; 436be60a902SHaavard Skinnemoen int retval; 437be60a902SHaavard Skinnemoen 43812d30aa7SHaavard Skinnemoen addr = flash_map (info, sect, offset); 439be60a902SHaavard Skinnemoen flash_make_cmd (info, cmd, &cword); 440be60a902SHaavard Skinnemoen switch (info->portwidth) { 441be60a902SHaavard Skinnemoen case FLASH_CFI_8BIT: 442cdbaefb5SHaavard Skinnemoen retval = ((flash_read8(addr) & cword.c) == cword.c); 443be60a902SHaavard Skinnemoen break; 444be60a902SHaavard Skinnemoen case FLASH_CFI_16BIT: 445cdbaefb5SHaavard Skinnemoen retval = ((flash_read16(addr) & cword.w) == cword.w); 446be60a902SHaavard Skinnemoen break; 447be60a902SHaavard Skinnemoen case FLASH_CFI_32BIT: 44847cc23cbSStefan Roese retval = ((flash_read32(addr) & cword.l) == cword.l); 449be60a902SHaavard Skinnemoen break; 450be60a902SHaavard Skinnemoen case FLASH_CFI_64BIT: 451cdbaefb5SHaavard Skinnemoen retval = ((flash_read64(addr) & cword.ll) == cword.ll); 452be60a902SHaavard Skinnemoen break; 453be60a902SHaavard Skinnemoen default: 454be60a902SHaavard Skinnemoen retval = 0; 455be60a902SHaavard Skinnemoen break; 456be60a902SHaavard Skinnemoen } 45712d30aa7SHaavard Skinnemoen flash_unmap(info, sect, offset, addr); 45812d30aa7SHaavard Skinnemoen 459be60a902SHaavard Skinnemoen return retval; 460be60a902SHaavard Skinnemoen } 461be60a902SHaavard Skinnemoen 462be60a902SHaavard Skinnemoen /*----------------------------------------------------------------------- 463be60a902SHaavard Skinnemoen */ 464be60a902SHaavard Skinnemoen static int flash_toggle (flash_info_t * info, flash_sect_t sect, 465be60a902SHaavard Skinnemoen uint offset, uchar cmd) 466be60a902SHaavard Skinnemoen { 467cdbaefb5SHaavard Skinnemoen void *addr; 468be60a902SHaavard Skinnemoen cfiword_t cword; 469be60a902SHaavard Skinnemoen int retval; 470be60a902SHaavard Skinnemoen 47112d30aa7SHaavard Skinnemoen addr = flash_map (info, sect, offset); 472be60a902SHaavard Skinnemoen flash_make_cmd (info, cmd, &cword); 473be60a902SHaavard Skinnemoen switch (info->portwidth) { 474be60a902SHaavard Skinnemoen case FLASH_CFI_8BIT: 475fb8c061eSStefan Roese retval = flash_read8(addr) != flash_read8(addr); 476be60a902SHaavard Skinnemoen break; 477be60a902SHaavard Skinnemoen case FLASH_CFI_16BIT: 478fb8c061eSStefan Roese retval = flash_read16(addr) != flash_read16(addr); 479be60a902SHaavard Skinnemoen break; 480be60a902SHaavard Skinnemoen case FLASH_CFI_32BIT: 481fb8c061eSStefan Roese retval = flash_read32(addr) != flash_read32(addr); 482be60a902SHaavard Skinnemoen break; 483be60a902SHaavard Skinnemoen case FLASH_CFI_64BIT: 4849abda6baSWolfgang Denk retval = ( (flash_read32( addr ) != flash_read32( addr )) || 4859abda6baSWolfgang Denk (flash_read32(addr+4) != flash_read32(addr+4)) ); 486be60a902SHaavard Skinnemoen break; 487be60a902SHaavard Skinnemoen default: 488be60a902SHaavard Skinnemoen retval = 0; 489be60a902SHaavard Skinnemoen break; 490be60a902SHaavard Skinnemoen } 49112d30aa7SHaavard Skinnemoen flash_unmap(info, sect, offset, addr); 49212d30aa7SHaavard Skinnemoen 493be60a902SHaavard Skinnemoen return retval; 494be60a902SHaavard Skinnemoen } 495be60a902SHaavard Skinnemoen 496be60a902SHaavard Skinnemoen /* 497be60a902SHaavard Skinnemoen * flash_is_busy - check to see if the flash is busy 498be60a902SHaavard Skinnemoen * 499be60a902SHaavard Skinnemoen * This routine checks the status of the chip and returns true if the 500be60a902SHaavard Skinnemoen * chip is busy. 501be60a902SHaavard Skinnemoen */ 502be60a902SHaavard Skinnemoen static int flash_is_busy (flash_info_t * info, flash_sect_t sect) 503be60a902SHaavard Skinnemoen { 504be60a902SHaavard Skinnemoen int retval; 505be60a902SHaavard Skinnemoen 50681b20cccSMichael Schwingen switch (info->vendor) { 5079c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 50881b20cccSMichael Schwingen case CFI_CMDSET_INTEL_STANDARD: 50981b20cccSMichael Schwingen case CFI_CMDSET_INTEL_EXTENDED: 510be60a902SHaavard Skinnemoen retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE); 51181b20cccSMichael Schwingen break; 51281b20cccSMichael Schwingen case CFI_CMDSET_AMD_STANDARD: 51381b20cccSMichael Schwingen case CFI_CMDSET_AMD_EXTENDED: 514be60a902SHaavard Skinnemoen #ifdef CONFIG_FLASH_CFI_LEGACY 51581b20cccSMichael Schwingen case CFI_CMDSET_AMD_LEGACY: 516be60a902SHaavard Skinnemoen #endif 517be60a902SHaavard Skinnemoen retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE); 518be60a902SHaavard Skinnemoen break; 519be60a902SHaavard Skinnemoen default: 520be60a902SHaavard Skinnemoen retval = 0; 521be60a902SHaavard Skinnemoen } 522be60a902SHaavard Skinnemoen debug ("flash_is_busy: %d\n", retval); 523be60a902SHaavard Skinnemoen return retval; 524be60a902SHaavard Skinnemoen } 525be60a902SHaavard Skinnemoen 526be60a902SHaavard Skinnemoen /*----------------------------------------------------------------------- 527be60a902SHaavard Skinnemoen * wait for XSR.7 to be set. Time out with an error if it does not. 528be60a902SHaavard Skinnemoen * This routine does not set the flash to read-array mode. 529be60a902SHaavard Skinnemoen */ 530be60a902SHaavard Skinnemoen static int flash_status_check (flash_info_t * info, flash_sect_t sector, 531be60a902SHaavard Skinnemoen ulong tout, char *prompt) 532be60a902SHaavard Skinnemoen { 533be60a902SHaavard Skinnemoen ulong start; 534be60a902SHaavard Skinnemoen 5356d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if CONFIG_SYS_HZ != 1000 536c40c94a3SRenato Andreola if ((ulong)CONFIG_SYS_HZ > 100000) 537c40c94a3SRenato Andreola tout *= (ulong)CONFIG_SYS_HZ / 1000; /* for a big HZ, avoid overflow */ 538c40c94a3SRenato Andreola else 539c40c94a3SRenato Andreola tout = DIV_ROUND_UP(tout * (ulong)CONFIG_SYS_HZ, 1000); 540be60a902SHaavard Skinnemoen #endif 541be60a902SHaavard Skinnemoen 542be60a902SHaavard Skinnemoen /* Wait for command completion */ 543e110c4feSGraeme Russ #ifdef CONFIG_SYS_LOW_RES_TIMER 54422d6c8faSThomas Chou reset_timer(); 545e110c4feSGraeme Russ #endif 546be60a902SHaavard Skinnemoen start = get_timer (0); 547a9f5fabaSJens Scharsig (BuS Elektronik) WATCHDOG_RESET(); 548be60a902SHaavard Skinnemoen while (flash_is_busy (info, sector)) { 549be60a902SHaavard Skinnemoen if (get_timer (start) > tout) { 550be60a902SHaavard Skinnemoen printf ("Flash %s timeout at address %lx data %lx\n", 551be60a902SHaavard Skinnemoen prompt, info->start[sector], 552be60a902SHaavard Skinnemoen flash_read_long (info, sector, 0)); 553be60a902SHaavard Skinnemoen flash_write_cmd (info, sector, 0, info->cmd_reset); 554e303be2dSStefan Roese udelay(1); 555be60a902SHaavard Skinnemoen return ERR_TIMOUT; 556be60a902SHaavard Skinnemoen } 557be60a902SHaavard Skinnemoen udelay (1); /* also triggers watchdog */ 558be60a902SHaavard Skinnemoen } 559be60a902SHaavard Skinnemoen return ERR_OK; 560be60a902SHaavard Skinnemoen } 561be60a902SHaavard Skinnemoen 562be60a902SHaavard Skinnemoen /*----------------------------------------------------------------------- 563be60a902SHaavard Skinnemoen * Wait for XSR.7 to be set, if it times out print an error, otherwise 564be60a902SHaavard Skinnemoen * do a full status check. 565be60a902SHaavard Skinnemoen * 566be60a902SHaavard Skinnemoen * This routine sets the flash to read-array mode. 567be60a902SHaavard Skinnemoen */ 568be60a902SHaavard Skinnemoen static int flash_full_status_check (flash_info_t * info, flash_sect_t sector, 569be60a902SHaavard Skinnemoen ulong tout, char *prompt) 570be60a902SHaavard Skinnemoen { 571be60a902SHaavard Skinnemoen int retcode; 572be60a902SHaavard Skinnemoen 573be60a902SHaavard Skinnemoen retcode = flash_status_check (info, sector, tout, prompt); 574be60a902SHaavard Skinnemoen switch (info->vendor) { 5759c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 576be60a902SHaavard Skinnemoen case CFI_CMDSET_INTEL_EXTENDED: 577be60a902SHaavard Skinnemoen case CFI_CMDSET_INTEL_STANDARD: 57891693055SBaruch Siach if ((retcode == ERR_OK) 579be60a902SHaavard Skinnemoen && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) { 580be60a902SHaavard Skinnemoen retcode = ERR_INVAL; 581be60a902SHaavard Skinnemoen printf ("Flash %s error at address %lx\n", prompt, 582be60a902SHaavard Skinnemoen info->start[sector]); 583be60a902SHaavard Skinnemoen if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS | 584be60a902SHaavard Skinnemoen FLASH_STATUS_PSLBS)) { 585be60a902SHaavard Skinnemoen puts ("Command Sequence Error.\n"); 586be60a902SHaavard Skinnemoen } else if (flash_isset (info, sector, 0, 587be60a902SHaavard Skinnemoen FLASH_STATUS_ECLBS)) { 588be60a902SHaavard Skinnemoen puts ("Block Erase Error.\n"); 589be60a902SHaavard Skinnemoen retcode = ERR_NOT_ERASED; 590be60a902SHaavard Skinnemoen } else if (flash_isset (info, sector, 0, 591be60a902SHaavard Skinnemoen FLASH_STATUS_PSLBS)) { 592be60a902SHaavard Skinnemoen puts ("Locking Error\n"); 593be60a902SHaavard Skinnemoen } 594be60a902SHaavard Skinnemoen if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) { 595be60a902SHaavard Skinnemoen puts ("Block locked.\n"); 596be60a902SHaavard Skinnemoen retcode = ERR_PROTECTED; 597be60a902SHaavard Skinnemoen } 598be60a902SHaavard Skinnemoen if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS)) 599be60a902SHaavard Skinnemoen puts ("Vpp Low Error.\n"); 600be60a902SHaavard Skinnemoen } 601be60a902SHaavard Skinnemoen flash_write_cmd (info, sector, 0, info->cmd_reset); 602a90b9575SAaron Williams udelay(1); 603be60a902SHaavard Skinnemoen break; 604be60a902SHaavard Skinnemoen default: 60581b20cccSMichael Schwingen break; 60681b20cccSMichael Schwingen } 607be60a902SHaavard Skinnemoen return retcode; 60881b20cccSMichael Schwingen } 609be60a902SHaavard Skinnemoen 610e5720823SThomas Chou static int use_flash_status_poll(flash_info_t *info) 611e5720823SThomas Chou { 612e5720823SThomas Chou #ifdef CONFIG_SYS_CFI_FLASH_STATUS_POLL 613e5720823SThomas Chou if (info->vendor == CFI_CMDSET_AMD_EXTENDED || 614e5720823SThomas Chou info->vendor == CFI_CMDSET_AMD_STANDARD) 615e5720823SThomas Chou return 1; 616e5720823SThomas Chou #endif 617e5720823SThomas Chou return 0; 618e5720823SThomas Chou } 619e5720823SThomas Chou 620e5720823SThomas Chou static int flash_status_poll(flash_info_t *info, void *src, void *dst, 621e5720823SThomas Chou ulong tout, char *prompt) 622e5720823SThomas Chou { 623e5720823SThomas Chou #ifdef CONFIG_SYS_CFI_FLASH_STATUS_POLL 624e5720823SThomas Chou ulong start; 625e5720823SThomas Chou int ready; 626e5720823SThomas Chou 627e5720823SThomas Chou #if CONFIG_SYS_HZ != 1000 628e5720823SThomas Chou if ((ulong)CONFIG_SYS_HZ > 100000) 629e5720823SThomas Chou tout *= (ulong)CONFIG_SYS_HZ / 1000; /* for a big HZ, avoid overflow */ 630e5720823SThomas Chou else 631e5720823SThomas Chou tout = DIV_ROUND_UP(tout * (ulong)CONFIG_SYS_HZ, 1000); 632e5720823SThomas Chou #endif 633e5720823SThomas Chou 634e5720823SThomas Chou /* Wait for command completion */ 635e110c4feSGraeme Russ #ifdef CONFIG_SYS_LOW_RES_TIMER 63622d6c8faSThomas Chou reset_timer(); 637e110c4feSGraeme Russ #endif 638e5720823SThomas Chou start = get_timer(0); 639a9f5fabaSJens Scharsig (BuS Elektronik) WATCHDOG_RESET(); 640e5720823SThomas Chou while (1) { 641e5720823SThomas Chou switch (info->portwidth) { 642e5720823SThomas Chou case FLASH_CFI_8BIT: 643e5720823SThomas Chou ready = flash_read8(dst) == flash_read8(src); 644e5720823SThomas Chou break; 645e5720823SThomas Chou case FLASH_CFI_16BIT: 646e5720823SThomas Chou ready = flash_read16(dst) == flash_read16(src); 647e5720823SThomas Chou break; 648e5720823SThomas Chou case FLASH_CFI_32BIT: 649e5720823SThomas Chou ready = flash_read32(dst) == flash_read32(src); 650e5720823SThomas Chou break; 651e5720823SThomas Chou case FLASH_CFI_64BIT: 652e5720823SThomas Chou ready = flash_read64(dst) == flash_read64(src); 653e5720823SThomas Chou break; 654e5720823SThomas Chou default: 655e5720823SThomas Chou ready = 0; 656e5720823SThomas Chou break; 657e5720823SThomas Chou } 658e5720823SThomas Chou if (ready) 659e5720823SThomas Chou break; 660e5720823SThomas Chou if (get_timer(start) > tout) { 661e5720823SThomas Chou printf("Flash %s timeout at address %lx data %lx\n", 662e5720823SThomas Chou prompt, (ulong)dst, (ulong)flash_read8(dst)); 663e5720823SThomas Chou return ERR_TIMOUT; 664e5720823SThomas Chou } 665e5720823SThomas Chou udelay(1); /* also triggers watchdog */ 666e5720823SThomas Chou } 667e5720823SThomas Chou #endif /* CONFIG_SYS_CFI_FLASH_STATUS_POLL */ 668e5720823SThomas Chou return ERR_OK; 669e5720823SThomas Chou } 670e5720823SThomas Chou 671be60a902SHaavard Skinnemoen /*----------------------------------------------------------------------- 672be60a902SHaavard Skinnemoen */ 673be60a902SHaavard Skinnemoen static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c) 674be60a902SHaavard Skinnemoen { 6756d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA) 676be60a902SHaavard Skinnemoen unsigned short w; 677be60a902SHaavard Skinnemoen unsigned int l; 678be60a902SHaavard Skinnemoen unsigned long long ll; 679be60a902SHaavard Skinnemoen #endif 680be60a902SHaavard Skinnemoen 681be60a902SHaavard Skinnemoen switch (info->portwidth) { 682be60a902SHaavard Skinnemoen case FLASH_CFI_8BIT: 683be60a902SHaavard Skinnemoen cword->c = c; 684be60a902SHaavard Skinnemoen break; 685be60a902SHaavard Skinnemoen case FLASH_CFI_16BIT: 6866d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA) 687be60a902SHaavard Skinnemoen w = c; 688be60a902SHaavard Skinnemoen w <<= 8; 689be60a902SHaavard Skinnemoen cword->w = (cword->w >> 8) | w; 69081b20cccSMichael Schwingen #else 691be60a902SHaavard Skinnemoen cword->w = (cword->w << 8) | c; 692be60a902SHaavard Skinnemoen #endif 693be60a902SHaavard Skinnemoen break; 694be60a902SHaavard Skinnemoen case FLASH_CFI_32BIT: 6956d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA) 696be60a902SHaavard Skinnemoen l = c; 697be60a902SHaavard Skinnemoen l <<= 24; 698be60a902SHaavard Skinnemoen cword->l = (cword->l >> 8) | l; 699be60a902SHaavard Skinnemoen #else 700be60a902SHaavard Skinnemoen cword->l = (cword->l << 8) | c; 701be60a902SHaavard Skinnemoen #endif 702be60a902SHaavard Skinnemoen break; 703be60a902SHaavard Skinnemoen case FLASH_CFI_64BIT: 7046d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA) 705be60a902SHaavard Skinnemoen ll = c; 706be60a902SHaavard Skinnemoen ll <<= 56; 707be60a902SHaavard Skinnemoen cword->ll = (cword->ll >> 8) | ll; 708be60a902SHaavard Skinnemoen #else 709be60a902SHaavard Skinnemoen cword->ll = (cword->ll << 8) | c; 710be60a902SHaavard Skinnemoen #endif 711be60a902SHaavard Skinnemoen break; 712be60a902SHaavard Skinnemoen } 713be60a902SHaavard Skinnemoen } 714be60a902SHaavard Skinnemoen 7150f8e851eSJens Gehrlein /* 7160f8e851eSJens Gehrlein * Loop through the sector table starting from the previously found sector. 7170f8e851eSJens Gehrlein * Searches forwards or backwards, dependent on the passed address. 718be60a902SHaavard Skinnemoen */ 719be60a902SHaavard Skinnemoen static flash_sect_t find_sector (flash_info_t * info, ulong addr) 72081b20cccSMichael Schwingen { 72111dc4010SKim Phillips static flash_sect_t saved_sector; /* previously found sector */ 722e303be2dSStefan Roese static flash_info_t *saved_info; /* previously used flash bank */ 7230f8e851eSJens Gehrlein flash_sect_t sector = saved_sector; 724be60a902SHaavard Skinnemoen 725e303be2dSStefan Roese if ((info != saved_info) || (sector >= info->sector_count)) 726e303be2dSStefan Roese sector = 0; 727e303be2dSStefan Roese 7280f8e851eSJens Gehrlein while ((info->start[sector] < addr) 7290f8e851eSJens Gehrlein && (sector < info->sector_count - 1)) 7300f8e851eSJens Gehrlein sector++; 7310f8e851eSJens Gehrlein while ((info->start[sector] > addr) && (sector > 0)) 7320f8e851eSJens Gehrlein /* 7330f8e851eSJens Gehrlein * also decrements the sector in case of an overshot 7340f8e851eSJens Gehrlein * in the first loop 7350f8e851eSJens Gehrlein */ 7360f8e851eSJens Gehrlein sector--; 7370f8e851eSJens Gehrlein 7380f8e851eSJens Gehrlein saved_sector = sector; 739e303be2dSStefan Roese saved_info = info; 740be60a902SHaavard Skinnemoen return sector; 74159829cc1SJean-Christophe PLAGNIOL-VILLARD } 74259829cc1SJean-Christophe PLAGNIOL-VILLARD 74359829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 74459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 745be60a902SHaavard Skinnemoen static int flash_write_cfiword (flash_info_t * info, ulong dest, 746be60a902SHaavard Skinnemoen cfiword_t cword) 74759829cc1SJean-Christophe PLAGNIOL-VILLARD { 74809ce9921SBecky Bruce void *dstaddr = (void *)dest; 749be60a902SHaavard Skinnemoen int flag; 750a7292871SJens Gehrlein flash_sect_t sect = 0; 751a7292871SJens Gehrlein char sect_found = 0; 75259829cc1SJean-Christophe PLAGNIOL-VILLARD 753be60a902SHaavard Skinnemoen /* Check if Flash is (sufficiently) erased */ 754be60a902SHaavard Skinnemoen switch (info->portwidth) { 755be60a902SHaavard Skinnemoen case FLASH_CFI_8BIT: 756cdbaefb5SHaavard Skinnemoen flag = ((flash_read8(dstaddr) & cword.c) == cword.c); 757be60a902SHaavard Skinnemoen break; 758be60a902SHaavard Skinnemoen case FLASH_CFI_16BIT: 759cdbaefb5SHaavard Skinnemoen flag = ((flash_read16(dstaddr) & cword.w) == cword.w); 760be60a902SHaavard Skinnemoen break; 761be60a902SHaavard Skinnemoen case FLASH_CFI_32BIT: 762cdbaefb5SHaavard Skinnemoen flag = ((flash_read32(dstaddr) & cword.l) == cword.l); 763be60a902SHaavard Skinnemoen break; 764be60a902SHaavard Skinnemoen case FLASH_CFI_64BIT: 765cdbaefb5SHaavard Skinnemoen flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll); 766be60a902SHaavard Skinnemoen break; 767be60a902SHaavard Skinnemoen default: 76812d30aa7SHaavard Skinnemoen flag = 0; 76912d30aa7SHaavard Skinnemoen break; 77012d30aa7SHaavard Skinnemoen } 77109ce9921SBecky Bruce if (!flag) 7720dc80e27SStefan Roese return ERR_NOT_ERASED; 773be60a902SHaavard Skinnemoen 774be60a902SHaavard Skinnemoen /* Disable interrupts which might cause a timeout here */ 775be60a902SHaavard Skinnemoen flag = disable_interrupts (); 776be60a902SHaavard Skinnemoen 777be60a902SHaavard Skinnemoen switch (info->vendor) { 7789c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 779be60a902SHaavard Skinnemoen case CFI_CMDSET_INTEL_EXTENDED: 780be60a902SHaavard Skinnemoen case CFI_CMDSET_INTEL_STANDARD: 781be60a902SHaavard Skinnemoen flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS); 782be60a902SHaavard Skinnemoen flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE); 783be60a902SHaavard Skinnemoen break; 784be60a902SHaavard Skinnemoen case CFI_CMDSET_AMD_EXTENDED: 785be60a902SHaavard Skinnemoen case CFI_CMDSET_AMD_STANDARD: 7860d01f66dSEd Swarthout sect = find_sector(info, dest); 7870d01f66dSEd Swarthout flash_unlock_seq (info, sect); 7880d01f66dSEd Swarthout flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_WRITE); 789a7292871SJens Gehrlein sect_found = 1; 79059829cc1SJean-Christophe PLAGNIOL-VILLARD break; 791b4db4a76SPo-Yu Chuang #ifdef CONFIG_FLASH_CFI_LEGACY 792b4db4a76SPo-Yu Chuang case CFI_CMDSET_AMD_LEGACY: 793b4db4a76SPo-Yu Chuang sect = find_sector(info, dest); 794b4db4a76SPo-Yu Chuang flash_unlock_seq (info, 0); 795b4db4a76SPo-Yu Chuang flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_WRITE); 796b4db4a76SPo-Yu Chuang sect_found = 1; 797b4db4a76SPo-Yu Chuang break; 798b4db4a76SPo-Yu Chuang #endif 79959829cc1SJean-Christophe PLAGNIOL-VILLARD } 80059829cc1SJean-Christophe PLAGNIOL-VILLARD 801be60a902SHaavard Skinnemoen switch (info->portwidth) { 802be60a902SHaavard Skinnemoen case FLASH_CFI_8BIT: 803cdbaefb5SHaavard Skinnemoen flash_write8(cword.c, dstaddr); 804be60a902SHaavard Skinnemoen break; 805be60a902SHaavard Skinnemoen case FLASH_CFI_16BIT: 806cdbaefb5SHaavard Skinnemoen flash_write16(cword.w, dstaddr); 807be60a902SHaavard Skinnemoen break; 808be60a902SHaavard Skinnemoen case FLASH_CFI_32BIT: 809cdbaefb5SHaavard Skinnemoen flash_write32(cword.l, dstaddr); 810be60a902SHaavard Skinnemoen break; 811be60a902SHaavard Skinnemoen case FLASH_CFI_64BIT: 812cdbaefb5SHaavard Skinnemoen flash_write64(cword.ll, dstaddr); 813be60a902SHaavard Skinnemoen break; 81459829cc1SJean-Christophe PLAGNIOL-VILLARD } 815be60a902SHaavard Skinnemoen 816be60a902SHaavard Skinnemoen /* re-enable interrupts if necessary */ 817be60a902SHaavard Skinnemoen if (flag) 818be60a902SHaavard Skinnemoen enable_interrupts (); 819be60a902SHaavard Skinnemoen 820a7292871SJens Gehrlein if (!sect_found) 821a7292871SJens Gehrlein sect = find_sector (info, dest); 822a7292871SJens Gehrlein 823e5720823SThomas Chou if (use_flash_status_poll(info)) 824e5720823SThomas Chou return flash_status_poll(info, &cword, dstaddr, 825e5720823SThomas Chou info->write_tout, "write"); 826e5720823SThomas Chou else 827e5720823SThomas Chou return flash_full_status_check(info, sect, 828e5720823SThomas Chou info->write_tout, "write"); 829be60a902SHaavard Skinnemoen } 830be60a902SHaavard Skinnemoen 8316d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE 832be60a902SHaavard Skinnemoen 833be60a902SHaavard Skinnemoen static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, 834be60a902SHaavard Skinnemoen int len) 835be60a902SHaavard Skinnemoen { 836be60a902SHaavard Skinnemoen flash_sect_t sector; 837be60a902SHaavard Skinnemoen int cnt; 838be60a902SHaavard Skinnemoen int retcode; 839cdbaefb5SHaavard Skinnemoen void *src = cp; 840ec21d5cfSStefan Roese void *dst = (void *)dest; 8410dc80e27SStefan Roese void *dst2 = dst; 84285c344e5STao Hou int flag = 1; 84396ef831fSGuennadi Liakhovetski uint offset = 0; 84496ef831fSGuennadi Liakhovetski unsigned int shift; 8459c048b52SVasiliy Leoenenko uchar write_cmd; 846cdbaefb5SHaavard Skinnemoen 8470dc80e27SStefan Roese switch (info->portwidth) { 8480dc80e27SStefan Roese case FLASH_CFI_8BIT: 84996ef831fSGuennadi Liakhovetski shift = 0; 8500dc80e27SStefan Roese break; 8510dc80e27SStefan Roese case FLASH_CFI_16BIT: 85296ef831fSGuennadi Liakhovetski shift = 1; 8530dc80e27SStefan Roese break; 8540dc80e27SStefan Roese case FLASH_CFI_32BIT: 85596ef831fSGuennadi Liakhovetski shift = 2; 8560dc80e27SStefan Roese break; 8570dc80e27SStefan Roese case FLASH_CFI_64BIT: 85896ef831fSGuennadi Liakhovetski shift = 3; 8590dc80e27SStefan Roese break; 8600dc80e27SStefan Roese default: 8610dc80e27SStefan Roese retcode = ERR_INVAL; 8620dc80e27SStefan Roese goto out_unmap; 8630dc80e27SStefan Roese } 8640dc80e27SStefan Roese 86596ef831fSGuennadi Liakhovetski cnt = len >> shift; 86696ef831fSGuennadi Liakhovetski 86785c344e5STao Hou while ((cnt-- > 0) && (flag == 1)) { 8680dc80e27SStefan Roese switch (info->portwidth) { 8690dc80e27SStefan Roese case FLASH_CFI_8BIT: 8700dc80e27SStefan Roese flag = ((flash_read8(dst2) & flash_read8(src)) == 8710dc80e27SStefan Roese flash_read8(src)); 8720dc80e27SStefan Roese src += 1, dst2 += 1; 8730dc80e27SStefan Roese break; 8740dc80e27SStefan Roese case FLASH_CFI_16BIT: 8750dc80e27SStefan Roese flag = ((flash_read16(dst2) & flash_read16(src)) == 8760dc80e27SStefan Roese flash_read16(src)); 8770dc80e27SStefan Roese src += 2, dst2 += 2; 8780dc80e27SStefan Roese break; 8790dc80e27SStefan Roese case FLASH_CFI_32BIT: 8800dc80e27SStefan Roese flag = ((flash_read32(dst2) & flash_read32(src)) == 8810dc80e27SStefan Roese flash_read32(src)); 8820dc80e27SStefan Roese src += 4, dst2 += 4; 8830dc80e27SStefan Roese break; 8840dc80e27SStefan Roese case FLASH_CFI_64BIT: 8850dc80e27SStefan Roese flag = ((flash_read64(dst2) & flash_read64(src)) == 8860dc80e27SStefan Roese flash_read64(src)); 8870dc80e27SStefan Roese src += 8, dst2 += 8; 8880dc80e27SStefan Roese break; 8890dc80e27SStefan Roese } 8900dc80e27SStefan Roese } 8910dc80e27SStefan Roese if (!flag) { 8920dc80e27SStefan Roese retcode = ERR_NOT_ERASED; 8930dc80e27SStefan Roese goto out_unmap; 8940dc80e27SStefan Roese } 8950dc80e27SStefan Roese 8960dc80e27SStefan Roese src = cp; 897cdbaefb5SHaavard Skinnemoen sector = find_sector (info, dest); 898be60a902SHaavard Skinnemoen 899be60a902SHaavard Skinnemoen switch (info->vendor) { 9009c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 901be60a902SHaavard Skinnemoen case CFI_CMDSET_INTEL_STANDARD: 902be60a902SHaavard Skinnemoen case CFI_CMDSET_INTEL_EXTENDED: 9039c048b52SVasiliy Leoenenko write_cmd = (info->vendor == CFI_CMDSET_INTEL_PROG_REGIONS) ? 9049c048b52SVasiliy Leoenenko FLASH_CMD_WRITE_BUFFER_PROG : FLASH_CMD_WRITE_TO_BUFFER; 905be60a902SHaavard Skinnemoen flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); 9069c048b52SVasiliy Leoenenko flash_write_cmd (info, sector, 0, FLASH_CMD_READ_STATUS); 9079c048b52SVasiliy Leoenenko flash_write_cmd (info, sector, 0, write_cmd); 908be60a902SHaavard Skinnemoen retcode = flash_status_check (info, sector, 909be60a902SHaavard Skinnemoen info->buffer_write_tout, 910be60a902SHaavard Skinnemoen "write to buffer"); 911be60a902SHaavard Skinnemoen if (retcode == ERR_OK) { 912be60a902SHaavard Skinnemoen /* reduce the number of loops by the width of 913be60a902SHaavard Skinnemoen * the port */ 91496ef831fSGuennadi Liakhovetski cnt = len >> shift; 91593c56f21SVasiliy Leoenenko flash_write_cmd (info, sector, 0, cnt - 1); 916be60a902SHaavard Skinnemoen while (cnt-- > 0) { 917be60a902SHaavard Skinnemoen switch (info->portwidth) { 918be60a902SHaavard Skinnemoen case FLASH_CFI_8BIT: 919cdbaefb5SHaavard Skinnemoen flash_write8(flash_read8(src), dst); 920cdbaefb5SHaavard Skinnemoen src += 1, dst += 1; 921be60a902SHaavard Skinnemoen break; 922be60a902SHaavard Skinnemoen case FLASH_CFI_16BIT: 923cdbaefb5SHaavard Skinnemoen flash_write16(flash_read16(src), dst); 924cdbaefb5SHaavard Skinnemoen src += 2, dst += 2; 925be60a902SHaavard Skinnemoen break; 926be60a902SHaavard Skinnemoen case FLASH_CFI_32BIT: 927cdbaefb5SHaavard Skinnemoen flash_write32(flash_read32(src), dst); 928cdbaefb5SHaavard Skinnemoen src += 4, dst += 4; 929be60a902SHaavard Skinnemoen break; 930be60a902SHaavard Skinnemoen case FLASH_CFI_64BIT: 931cdbaefb5SHaavard Skinnemoen flash_write64(flash_read64(src), dst); 932cdbaefb5SHaavard Skinnemoen src += 8, dst += 8; 933be60a902SHaavard Skinnemoen break; 934be60a902SHaavard Skinnemoen default: 93512d30aa7SHaavard Skinnemoen retcode = ERR_INVAL; 93612d30aa7SHaavard Skinnemoen goto out_unmap; 937be60a902SHaavard Skinnemoen } 938be60a902SHaavard Skinnemoen } 939be60a902SHaavard Skinnemoen flash_write_cmd (info, sector, 0, 940be60a902SHaavard Skinnemoen FLASH_CMD_WRITE_BUFFER_CONFIRM); 941be60a902SHaavard Skinnemoen retcode = flash_full_status_check ( 942be60a902SHaavard Skinnemoen info, sector, info->buffer_write_tout, 943be60a902SHaavard Skinnemoen "buffer write"); 944be60a902SHaavard Skinnemoen } 94512d30aa7SHaavard Skinnemoen 94612d30aa7SHaavard Skinnemoen break; 947be60a902SHaavard Skinnemoen 948be60a902SHaavard Skinnemoen case CFI_CMDSET_AMD_STANDARD: 949be60a902SHaavard Skinnemoen case CFI_CMDSET_AMD_EXTENDED: 950be60a902SHaavard Skinnemoen flash_unlock_seq(info,0); 95196ef831fSGuennadi Liakhovetski 95296ef831fSGuennadi Liakhovetski #ifdef CONFIG_FLASH_SPANSION_S29WS_N 95396ef831fSGuennadi Liakhovetski offset = ((unsigned long)dst - info->start[sector]) >> shift; 95496ef831fSGuennadi Liakhovetski #endif 95596ef831fSGuennadi Liakhovetski flash_write_cmd(info, sector, offset, AMD_CMD_WRITE_TO_BUFFER); 95696ef831fSGuennadi Liakhovetski cnt = len >> shift; 9577dedefdfSJohn Schmoller flash_write_cmd(info, sector, offset, cnt - 1); 958be60a902SHaavard Skinnemoen 959be60a902SHaavard Skinnemoen switch (info->portwidth) { 960be60a902SHaavard Skinnemoen case FLASH_CFI_8BIT: 961cdbaefb5SHaavard Skinnemoen while (cnt-- > 0) { 962cdbaefb5SHaavard Skinnemoen flash_write8(flash_read8(src), dst); 963cdbaefb5SHaavard Skinnemoen src += 1, dst += 1; 964cdbaefb5SHaavard Skinnemoen } 965be60a902SHaavard Skinnemoen break; 966be60a902SHaavard Skinnemoen case FLASH_CFI_16BIT: 967cdbaefb5SHaavard Skinnemoen while (cnt-- > 0) { 968cdbaefb5SHaavard Skinnemoen flash_write16(flash_read16(src), dst); 969cdbaefb5SHaavard Skinnemoen src += 2, dst += 2; 970cdbaefb5SHaavard Skinnemoen } 971be60a902SHaavard Skinnemoen break; 972be60a902SHaavard Skinnemoen case FLASH_CFI_32BIT: 973cdbaefb5SHaavard Skinnemoen while (cnt-- > 0) { 974cdbaefb5SHaavard Skinnemoen flash_write32(flash_read32(src), dst); 975cdbaefb5SHaavard Skinnemoen src += 4, dst += 4; 976cdbaefb5SHaavard Skinnemoen } 977be60a902SHaavard Skinnemoen break; 978be60a902SHaavard Skinnemoen case FLASH_CFI_64BIT: 979cdbaefb5SHaavard Skinnemoen while (cnt-- > 0) { 980cdbaefb5SHaavard Skinnemoen flash_write64(flash_read64(src), dst); 981cdbaefb5SHaavard Skinnemoen src += 8, dst += 8; 982cdbaefb5SHaavard Skinnemoen } 983be60a902SHaavard Skinnemoen break; 984be60a902SHaavard Skinnemoen default: 98512d30aa7SHaavard Skinnemoen retcode = ERR_INVAL; 98612d30aa7SHaavard Skinnemoen goto out_unmap; 987be60a902SHaavard Skinnemoen } 988be60a902SHaavard Skinnemoen 989be60a902SHaavard Skinnemoen flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM); 990e5720823SThomas Chou if (use_flash_status_poll(info)) 991e5720823SThomas Chou retcode = flash_status_poll(info, src - (1 << shift), 992e5720823SThomas Chou dst - (1 << shift), 993e5720823SThomas Chou info->buffer_write_tout, 994e5720823SThomas Chou "buffer write"); 995e5720823SThomas Chou else 996be60a902SHaavard Skinnemoen retcode = flash_full_status_check(info, sector, 997be60a902SHaavard Skinnemoen info->buffer_write_tout, 998be60a902SHaavard Skinnemoen "buffer write"); 99912d30aa7SHaavard Skinnemoen break; 1000be60a902SHaavard Skinnemoen 1001be60a902SHaavard Skinnemoen default: 1002be60a902SHaavard Skinnemoen debug ("Unknown Command Set\n"); 100312d30aa7SHaavard Skinnemoen retcode = ERR_INVAL; 100412d30aa7SHaavard Skinnemoen break; 1005be60a902SHaavard Skinnemoen } 100612d30aa7SHaavard Skinnemoen 100712d30aa7SHaavard Skinnemoen out_unmap: 100812d30aa7SHaavard Skinnemoen return retcode; 1009be60a902SHaavard Skinnemoen } 10106d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */ 1011be60a902SHaavard Skinnemoen 101259829cc1SJean-Christophe PLAGNIOL-VILLARD 101359829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 101459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 101559829cc1SJean-Christophe PLAGNIOL-VILLARD int flash_erase (flash_info_t * info, int s_first, int s_last) 101659829cc1SJean-Christophe PLAGNIOL-VILLARD { 101759829cc1SJean-Christophe PLAGNIOL-VILLARD int rcode = 0; 101859829cc1SJean-Christophe PLAGNIOL-VILLARD int prot; 101959829cc1SJean-Christophe PLAGNIOL-VILLARD flash_sect_t sect; 1020e5720823SThomas Chou int st; 102159829cc1SJean-Christophe PLAGNIOL-VILLARD 102259829cc1SJean-Christophe PLAGNIOL-VILLARD if (info->flash_id != FLASH_MAN_CFI) { 102359829cc1SJean-Christophe PLAGNIOL-VILLARD puts ("Can't erase unknown flash type - aborted\n"); 102459829cc1SJean-Christophe PLAGNIOL-VILLARD return 1; 102559829cc1SJean-Christophe PLAGNIOL-VILLARD } 102659829cc1SJean-Christophe PLAGNIOL-VILLARD if ((s_first < 0) || (s_first > s_last)) { 102759829cc1SJean-Christophe PLAGNIOL-VILLARD puts ("- no sectors to erase\n"); 102859829cc1SJean-Christophe PLAGNIOL-VILLARD return 1; 102959829cc1SJean-Christophe PLAGNIOL-VILLARD } 103059829cc1SJean-Christophe PLAGNIOL-VILLARD 103159829cc1SJean-Christophe PLAGNIOL-VILLARD prot = 0; 103259829cc1SJean-Christophe PLAGNIOL-VILLARD for (sect = s_first; sect <= s_last; ++sect) { 103359829cc1SJean-Christophe PLAGNIOL-VILLARD if (info->protect[sect]) { 103459829cc1SJean-Christophe PLAGNIOL-VILLARD prot++; 103559829cc1SJean-Christophe PLAGNIOL-VILLARD } 103659829cc1SJean-Christophe PLAGNIOL-VILLARD } 103759829cc1SJean-Christophe PLAGNIOL-VILLARD if (prot) { 10387e5b9b47SHaavard Skinnemoen printf ("- Warning: %d protected sectors will not be erased!\n", 10397e5b9b47SHaavard Skinnemoen prot); 10406ea808efSPiotr Ziecik } else if (flash_verbose) { 104159829cc1SJean-Christophe PLAGNIOL-VILLARD putc ('\n'); 104259829cc1SJean-Christophe PLAGNIOL-VILLARD } 104359829cc1SJean-Christophe PLAGNIOL-VILLARD 104459829cc1SJean-Christophe PLAGNIOL-VILLARD 104559829cc1SJean-Christophe PLAGNIOL-VILLARD for (sect = s_first; sect <= s_last; sect++) { 1046de15a06aSJoe Hershberger if (ctrlc()) { 1047de15a06aSJoe Hershberger printf("\n"); 1048de15a06aSJoe Hershberger return 1; 1049de15a06aSJoe Hershberger } 1050de15a06aSJoe Hershberger 105159829cc1SJean-Christophe PLAGNIOL-VILLARD if (info->protect[sect] == 0) { /* not protected */ 10526822a647SJoe Hershberger #ifdef CONFIG_SYS_FLASH_CHECK_BLANK_BEFORE_ERASE 10536822a647SJoe Hershberger int k; 10546822a647SJoe Hershberger int size; 10556822a647SJoe Hershberger int erased; 10566822a647SJoe Hershberger u32 *flash; 10576822a647SJoe Hershberger 10586822a647SJoe Hershberger /* 10596822a647SJoe Hershberger * Check if whole sector is erased 10606822a647SJoe Hershberger */ 10616822a647SJoe Hershberger size = flash_sector_size(info, sect); 10626822a647SJoe Hershberger erased = 1; 10636822a647SJoe Hershberger flash = (u32 *)info->start[sect]; 10646822a647SJoe Hershberger /* divide by 4 for longword access */ 10656822a647SJoe Hershberger size = size >> 2; 10666822a647SJoe Hershberger for (k = 0; k < size; k++) { 10676822a647SJoe Hershberger if (flash_read32(flash++) != 0xffffffff) { 10686822a647SJoe Hershberger erased = 0; 10696822a647SJoe Hershberger break; 10706822a647SJoe Hershberger } 10716822a647SJoe Hershberger } 10726822a647SJoe Hershberger if (erased) { 10736822a647SJoe Hershberger if (flash_verbose) 10746822a647SJoe Hershberger putc(','); 10756822a647SJoe Hershberger continue; 10766822a647SJoe Hershberger } 10776822a647SJoe Hershberger #endif 107859829cc1SJean-Christophe PLAGNIOL-VILLARD switch (info->vendor) { 10799c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 108059829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_INTEL_STANDARD: 108159829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_INTEL_EXTENDED: 10827e5b9b47SHaavard Skinnemoen flash_write_cmd (info, sect, 0, 10837e5b9b47SHaavard Skinnemoen FLASH_CMD_CLEAR_STATUS); 10847e5b9b47SHaavard Skinnemoen flash_write_cmd (info, sect, 0, 10857e5b9b47SHaavard Skinnemoen FLASH_CMD_BLOCK_ERASE); 10867e5b9b47SHaavard Skinnemoen flash_write_cmd (info, sect, 0, 10877e5b9b47SHaavard Skinnemoen FLASH_CMD_ERASE_CONFIRM); 108859829cc1SJean-Christophe PLAGNIOL-VILLARD break; 108959829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_AMD_STANDARD: 109059829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_AMD_EXTENDED: 109159829cc1SJean-Christophe PLAGNIOL-VILLARD flash_unlock_seq (info, sect); 10927e5b9b47SHaavard Skinnemoen flash_write_cmd (info, sect, 10937e5b9b47SHaavard Skinnemoen info->addr_unlock1, 10947e5b9b47SHaavard Skinnemoen AMD_CMD_ERASE_START); 109559829cc1SJean-Christophe PLAGNIOL-VILLARD flash_unlock_seq (info, sect); 10967e5b9b47SHaavard Skinnemoen flash_write_cmd (info, sect, 0, 109707b2c5c0SAngelo Dureghello info->cmd_erase_sector); 109859829cc1SJean-Christophe PLAGNIOL-VILLARD break; 109981b20cccSMichael Schwingen #ifdef CONFIG_FLASH_CFI_LEGACY 110081b20cccSMichael Schwingen case CFI_CMDSET_AMD_LEGACY: 110181b20cccSMichael Schwingen flash_unlock_seq (info, 0); 11027e5b9b47SHaavard Skinnemoen flash_write_cmd (info, 0, info->addr_unlock1, 11037e5b9b47SHaavard Skinnemoen AMD_CMD_ERASE_START); 110481b20cccSMichael Schwingen flash_unlock_seq (info, 0); 11057e5b9b47SHaavard Skinnemoen flash_write_cmd (info, sect, 0, 11067e5b9b47SHaavard Skinnemoen AMD_CMD_ERASE_SECTOR); 110781b20cccSMichael Schwingen break; 110881b20cccSMichael Schwingen #endif 110959829cc1SJean-Christophe PLAGNIOL-VILLARD default: 111059829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("Unkown flash vendor %d\n", 111159829cc1SJean-Christophe PLAGNIOL-VILLARD info->vendor); 111259829cc1SJean-Christophe PLAGNIOL-VILLARD break; 111359829cc1SJean-Christophe PLAGNIOL-VILLARD } 111459829cc1SJean-Christophe PLAGNIOL-VILLARD 1115e5720823SThomas Chou if (use_flash_status_poll(info)) { 111611dc4010SKim Phillips cfiword_t cword; 1117e5720823SThomas Chou void *dest; 111811dc4010SKim Phillips cword.ll = 0xffffffffffffffffULL; 1119e5720823SThomas Chou dest = flash_map(info, sect, 0); 1120e5720823SThomas Chou st = flash_status_poll(info, &cword, dest, 1121e5720823SThomas Chou info->erase_blk_tout, "erase"); 1122e5720823SThomas Chou flash_unmap(info, sect, 0, dest); 1123e5720823SThomas Chou } else 1124e5720823SThomas Chou st = flash_full_status_check(info, sect, 1125e5720823SThomas Chou info->erase_blk_tout, 1126e5720823SThomas Chou "erase"); 1127e5720823SThomas Chou if (st) 112859829cc1SJean-Christophe PLAGNIOL-VILLARD rcode = 1; 1129e5720823SThomas Chou else if (flash_verbose) 113059829cc1SJean-Christophe PLAGNIOL-VILLARD putc ('.'); 113159829cc1SJean-Christophe PLAGNIOL-VILLARD } 113259829cc1SJean-Christophe PLAGNIOL-VILLARD } 11336ea808efSPiotr Ziecik 11346ea808efSPiotr Ziecik if (flash_verbose) 113559829cc1SJean-Christophe PLAGNIOL-VILLARD puts (" done\n"); 11366ea808efSPiotr Ziecik 113759829cc1SJean-Christophe PLAGNIOL-VILLARD return rcode; 113859829cc1SJean-Christophe PLAGNIOL-VILLARD } 113959829cc1SJean-Christophe PLAGNIOL-VILLARD 114070084df7SStefan Roese #ifdef CONFIG_SYS_FLASH_EMPTY_INFO 114170084df7SStefan Roese static int sector_erased(flash_info_t *info, int i) 114270084df7SStefan Roese { 114370084df7SStefan Roese int k; 114470084df7SStefan Roese int size; 11454d2ca9d6SStefan Roese u32 *flash; 114670084df7SStefan Roese 114770084df7SStefan Roese /* 114870084df7SStefan Roese * Check if whole sector is erased 114959829cc1SJean-Christophe PLAGNIOL-VILLARD */ 115070084df7SStefan Roese size = flash_sector_size(info, i); 11514d2ca9d6SStefan Roese flash = (u32 *)info->start[i]; 115270084df7SStefan Roese /* divide by 4 for longword access */ 115370084df7SStefan Roese size = size >> 2; 115470084df7SStefan Roese 115570084df7SStefan Roese for (k = 0; k < size; k++) { 11564d2ca9d6SStefan Roese if (flash_read32(flash++) != 0xffffffff) 115770084df7SStefan Roese return 0; /* not erased */ 115870084df7SStefan Roese } 115970084df7SStefan Roese 116070084df7SStefan Roese return 1; /* erased */ 116170084df7SStefan Roese } 116270084df7SStefan Roese #endif /* CONFIG_SYS_FLASH_EMPTY_INFO */ 116370084df7SStefan Roese 116459829cc1SJean-Christophe PLAGNIOL-VILLARD void flash_print_info (flash_info_t * info) 116559829cc1SJean-Christophe PLAGNIOL-VILLARD { 116659829cc1SJean-Christophe PLAGNIOL-VILLARD int i; 116759829cc1SJean-Christophe PLAGNIOL-VILLARD 116859829cc1SJean-Christophe PLAGNIOL-VILLARD if (info->flash_id != FLASH_MAN_CFI) { 116959829cc1SJean-Christophe PLAGNIOL-VILLARD puts ("missing or unknown FLASH type\n"); 117059829cc1SJean-Christophe PLAGNIOL-VILLARD return; 117159829cc1SJean-Christophe PLAGNIOL-VILLARD } 117259829cc1SJean-Christophe PLAGNIOL-VILLARD 1173eddf52b5SPeter Tyser printf ("%s flash (%d x %d)", 117481b20cccSMichael Schwingen info->name, 117559829cc1SJean-Christophe PLAGNIOL-VILLARD (info->portwidth << 3), (info->chipwidth << 3)); 117681b20cccSMichael Schwingen if (info->size < 1024*1024) 117781b20cccSMichael Schwingen printf (" Size: %ld kB in %d Sectors\n", 117881b20cccSMichael Schwingen info->size >> 10, info->sector_count); 117981b20cccSMichael Schwingen else 118059829cc1SJean-Christophe PLAGNIOL-VILLARD printf (" Size: %ld MB in %d Sectors\n", 118159829cc1SJean-Christophe PLAGNIOL-VILLARD info->size >> 20, info->sector_count); 118259829cc1SJean-Christophe PLAGNIOL-VILLARD printf (" "); 118359829cc1SJean-Christophe PLAGNIOL-VILLARD switch (info->vendor) { 11849c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 11859c048b52SVasiliy Leoenenko printf ("Intel Prog Regions"); 11869c048b52SVasiliy Leoenenko break; 118759829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_INTEL_STANDARD: 118859829cc1SJean-Christophe PLAGNIOL-VILLARD printf ("Intel Standard"); 118959829cc1SJean-Christophe PLAGNIOL-VILLARD break; 119059829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_INTEL_EXTENDED: 119159829cc1SJean-Christophe PLAGNIOL-VILLARD printf ("Intel Extended"); 119259829cc1SJean-Christophe PLAGNIOL-VILLARD break; 119359829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_AMD_STANDARD: 119459829cc1SJean-Christophe PLAGNIOL-VILLARD printf ("AMD Standard"); 119559829cc1SJean-Christophe PLAGNIOL-VILLARD break; 119659829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_AMD_EXTENDED: 119759829cc1SJean-Christophe PLAGNIOL-VILLARD printf ("AMD Extended"); 119859829cc1SJean-Christophe PLAGNIOL-VILLARD break; 119981b20cccSMichael Schwingen #ifdef CONFIG_FLASH_CFI_LEGACY 120081b20cccSMichael Schwingen case CFI_CMDSET_AMD_LEGACY: 120181b20cccSMichael Schwingen printf ("AMD Legacy"); 120281b20cccSMichael Schwingen break; 120381b20cccSMichael Schwingen #endif 120459829cc1SJean-Christophe PLAGNIOL-VILLARD default: 120559829cc1SJean-Christophe PLAGNIOL-VILLARD printf ("Unknown (%d)", info->vendor); 120659829cc1SJean-Christophe PLAGNIOL-VILLARD break; 120759829cc1SJean-Christophe PLAGNIOL-VILLARD } 1208d77c7ac4SPhilippe De Muyter printf (" command set, Manufacturer ID: 0x%02X, Device ID: 0x", 1209d77c7ac4SPhilippe De Muyter info->manufacturer_id); 1210d77c7ac4SPhilippe De Muyter printf (info->chipwidth == FLASH_CFI_16BIT ? "%04X" : "%02X", 1211d77c7ac4SPhilippe De Muyter info->device_id); 12125b448adbSHeiko Schocher if ((info->device_id & 0xff) == 0x7E) { 12135b448adbSHeiko Schocher printf(info->chipwidth == FLASH_CFI_16BIT ? "%04X" : "%02X", 12145b448adbSHeiko Schocher info->device_id2); 121559829cc1SJean-Christophe PLAGNIOL-VILLARD } 1216d2af028dSStefan Roese if ((info->vendor == CFI_CMDSET_AMD_STANDARD) && (info->legacy_unlock)) 1217d2af028dSStefan Roese printf("\n Advanced Sector Protection (PPB) enabled"); 121859829cc1SJean-Christophe PLAGNIOL-VILLARD printf ("\n Erase timeout: %ld ms, write timeout: %ld ms\n", 121959829cc1SJean-Christophe PLAGNIOL-VILLARD info->erase_blk_tout, 122059829cc1SJean-Christophe PLAGNIOL-VILLARD info->write_tout); 122159829cc1SJean-Christophe PLAGNIOL-VILLARD if (info->buffer_size > 1) { 12227e5b9b47SHaavard Skinnemoen printf (" Buffer write timeout: %ld ms, " 12237e5b9b47SHaavard Skinnemoen "buffer size: %d bytes\n", 122459829cc1SJean-Christophe PLAGNIOL-VILLARD info->buffer_write_tout, 122559829cc1SJean-Christophe PLAGNIOL-VILLARD info->buffer_size); 122659829cc1SJean-Christophe PLAGNIOL-VILLARD } 122759829cc1SJean-Christophe PLAGNIOL-VILLARD 122859829cc1SJean-Christophe PLAGNIOL-VILLARD puts ("\n Sector Start Addresses:"); 122959829cc1SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < info->sector_count; ++i) { 12302e97394aSKim Phillips if (ctrlc()) 123159829cc1SJean-Christophe PLAGNIOL-VILLARD break; 123270084df7SStefan Roese if ((i % 5) == 0) 123370084df7SStefan Roese putc('\n'); 123470084df7SStefan Roese #ifdef CONFIG_SYS_FLASH_EMPTY_INFO 123559829cc1SJean-Christophe PLAGNIOL-VILLARD /* print empty and read-only info */ 123659829cc1SJean-Christophe PLAGNIOL-VILLARD printf (" %08lX %c %s ", 123759829cc1SJean-Christophe PLAGNIOL-VILLARD info->start[i], 123870084df7SStefan Roese sector_erased(info, i) ? 'E' : ' ', 123959829cc1SJean-Christophe PLAGNIOL-VILLARD info->protect[i] ? "RO" : " "); 12406d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #else /* ! CONFIG_SYS_FLASH_EMPTY_INFO */ 124159829cc1SJean-Christophe PLAGNIOL-VILLARD printf (" %08lX %s ", 124259829cc1SJean-Christophe PLAGNIOL-VILLARD info->start[i], 124359829cc1SJean-Christophe PLAGNIOL-VILLARD info->protect[i] ? "RO" : " "); 124459829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 124559829cc1SJean-Christophe PLAGNIOL-VILLARD } 124659829cc1SJean-Christophe PLAGNIOL-VILLARD putc ('\n'); 124759829cc1SJean-Christophe PLAGNIOL-VILLARD return; 124859829cc1SJean-Christophe PLAGNIOL-VILLARD } 124959829cc1SJean-Christophe PLAGNIOL-VILLARD 125059829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 12519a042e9cSJerry Van Baren * This is used in a few places in write_buf() to show programming 12529a042e9cSJerry Van Baren * progress. Making it a function is nasty because it needs to do side 12539a042e9cSJerry Van Baren * effect updates to digit and dots. Repeated code is nasty too, so 12549a042e9cSJerry Van Baren * we define it once here. 12559a042e9cSJerry Van Baren */ 1256f0105727SStefan Roese #ifdef CONFIG_FLASH_SHOW_PROGRESS 1257f0105727SStefan Roese #define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub) \ 12586ea808efSPiotr Ziecik if (flash_verbose) { \ 1259f0105727SStefan Roese dots -= dots_sub; \ 12609a042e9cSJerry Van Baren if ((scale > 0) && (dots <= 0)) { \ 12619a042e9cSJerry Van Baren if ((digit % 5) == 0) \ 12629a042e9cSJerry Van Baren printf ("%d", digit / 5); \ 12639a042e9cSJerry Van Baren else \ 12649a042e9cSJerry Van Baren putc ('.'); \ 12659a042e9cSJerry Van Baren digit--; \ 12669a042e9cSJerry Van Baren dots += scale; \ 12676ea808efSPiotr Ziecik } \ 12689a042e9cSJerry Van Baren } 1269f0105727SStefan Roese #else 1270f0105727SStefan Roese #define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub) 1271f0105727SStefan Roese #endif 12729a042e9cSJerry Van Baren 12739a042e9cSJerry Van Baren /*----------------------------------------------------------------------- 127459829cc1SJean-Christophe PLAGNIOL-VILLARD * Copy memory to flash, returns: 127559829cc1SJean-Christophe PLAGNIOL-VILLARD * 0 - OK 127659829cc1SJean-Christophe PLAGNIOL-VILLARD * 1 - write timeout 127759829cc1SJean-Christophe PLAGNIOL-VILLARD * 2 - Flash not erased 127859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 127959829cc1SJean-Christophe PLAGNIOL-VILLARD int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) 128059829cc1SJean-Christophe PLAGNIOL-VILLARD { 128159829cc1SJean-Christophe PLAGNIOL-VILLARD ulong wp; 128212d30aa7SHaavard Skinnemoen uchar *p; 128359829cc1SJean-Christophe PLAGNIOL-VILLARD int aln; 128459829cc1SJean-Christophe PLAGNIOL-VILLARD cfiword_t cword; 128559829cc1SJean-Christophe PLAGNIOL-VILLARD int i, rc; 12866d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE 128759829cc1SJean-Christophe PLAGNIOL-VILLARD int buffered_size; 128859829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 12899a042e9cSJerry Van Baren #ifdef CONFIG_FLASH_SHOW_PROGRESS 12909a042e9cSJerry Van Baren int digit = CONFIG_FLASH_SHOW_PROGRESS; 12919a042e9cSJerry Van Baren int scale = 0; 12929a042e9cSJerry Van Baren int dots = 0; 12939a042e9cSJerry Van Baren 12949a042e9cSJerry Van Baren /* 12959a042e9cSJerry Van Baren * Suppress if there are fewer than CONFIG_FLASH_SHOW_PROGRESS writes. 12969a042e9cSJerry Van Baren */ 12979a042e9cSJerry Van Baren if (cnt >= CONFIG_FLASH_SHOW_PROGRESS) { 12989a042e9cSJerry Van Baren scale = (int)((cnt + CONFIG_FLASH_SHOW_PROGRESS - 1) / 12999a042e9cSJerry Van Baren CONFIG_FLASH_SHOW_PROGRESS); 13009a042e9cSJerry Van Baren } 13019a042e9cSJerry Van Baren #endif 13029a042e9cSJerry Van Baren 130359829cc1SJean-Christophe PLAGNIOL-VILLARD /* get lower aligned address */ 130459829cc1SJean-Christophe PLAGNIOL-VILLARD wp = (addr & ~(info->portwidth - 1)); 130559829cc1SJean-Christophe PLAGNIOL-VILLARD 130659829cc1SJean-Christophe PLAGNIOL-VILLARD /* handle unaligned start */ 130759829cc1SJean-Christophe PLAGNIOL-VILLARD if ((aln = addr - wp) != 0) { 130859829cc1SJean-Christophe PLAGNIOL-VILLARD cword.l = 0; 130909ce9921SBecky Bruce p = (uchar *)wp; 131012d30aa7SHaavard Skinnemoen for (i = 0; i < aln; ++i) 131112d30aa7SHaavard Skinnemoen flash_add_byte (info, &cword, flash_read8(p + i)); 131259829cc1SJean-Christophe PLAGNIOL-VILLARD 131359829cc1SJean-Christophe PLAGNIOL-VILLARD for (; (i < info->portwidth) && (cnt > 0); i++) { 131459829cc1SJean-Christophe PLAGNIOL-VILLARD flash_add_byte (info, &cword, *src++); 131559829cc1SJean-Christophe PLAGNIOL-VILLARD cnt--; 131659829cc1SJean-Christophe PLAGNIOL-VILLARD } 131712d30aa7SHaavard Skinnemoen for (; (cnt == 0) && (i < info->portwidth); ++i) 131812d30aa7SHaavard Skinnemoen flash_add_byte (info, &cword, flash_read8(p + i)); 131912d30aa7SHaavard Skinnemoen 132012d30aa7SHaavard Skinnemoen rc = flash_write_cfiword (info, wp, cword); 132112d30aa7SHaavard Skinnemoen if (rc != 0) 132259829cc1SJean-Christophe PLAGNIOL-VILLARD return rc; 132312d30aa7SHaavard Skinnemoen 132412d30aa7SHaavard Skinnemoen wp += i; 1325f0105727SStefan Roese FLASH_SHOW_PROGRESS(scale, dots, digit, i); 132659829cc1SJean-Christophe PLAGNIOL-VILLARD } 132759829cc1SJean-Christophe PLAGNIOL-VILLARD 132859829cc1SJean-Christophe PLAGNIOL-VILLARD /* handle the aligned part */ 13296d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE 133059829cc1SJean-Christophe PLAGNIOL-VILLARD buffered_size = (info->portwidth / info->chipwidth); 133159829cc1SJean-Christophe PLAGNIOL-VILLARD buffered_size *= info->buffer_size; 133259829cc1SJean-Christophe PLAGNIOL-VILLARD while (cnt >= info->portwidth) { 133359829cc1SJean-Christophe PLAGNIOL-VILLARD /* prohibit buffer write when buffer_size is 1 */ 133459829cc1SJean-Christophe PLAGNIOL-VILLARD if (info->buffer_size == 1) { 133559829cc1SJean-Christophe PLAGNIOL-VILLARD cword.l = 0; 133659829cc1SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < info->portwidth; i++) 133759829cc1SJean-Christophe PLAGNIOL-VILLARD flash_add_byte (info, &cword, *src++); 133859829cc1SJean-Christophe PLAGNIOL-VILLARD if ((rc = flash_write_cfiword (info, wp, cword)) != 0) 133959829cc1SJean-Christophe PLAGNIOL-VILLARD return rc; 134059829cc1SJean-Christophe PLAGNIOL-VILLARD wp += info->portwidth; 134159829cc1SJean-Christophe PLAGNIOL-VILLARD cnt -= info->portwidth; 134259829cc1SJean-Christophe PLAGNIOL-VILLARD continue; 134359829cc1SJean-Christophe PLAGNIOL-VILLARD } 134459829cc1SJean-Christophe PLAGNIOL-VILLARD 134559829cc1SJean-Christophe PLAGNIOL-VILLARD /* write buffer until next buffered_size aligned boundary */ 134659829cc1SJean-Christophe PLAGNIOL-VILLARD i = buffered_size - (wp % buffered_size); 134759829cc1SJean-Christophe PLAGNIOL-VILLARD if (i > cnt) 134859829cc1SJean-Christophe PLAGNIOL-VILLARD i = cnt; 134959829cc1SJean-Christophe PLAGNIOL-VILLARD if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK) 135059829cc1SJean-Christophe PLAGNIOL-VILLARD return rc; 135159829cc1SJean-Christophe PLAGNIOL-VILLARD i -= i & (info->portwidth - 1); 135259829cc1SJean-Christophe PLAGNIOL-VILLARD wp += i; 135359829cc1SJean-Christophe PLAGNIOL-VILLARD src += i; 135459829cc1SJean-Christophe PLAGNIOL-VILLARD cnt -= i; 1355f0105727SStefan Roese FLASH_SHOW_PROGRESS(scale, dots, digit, i); 1356de15a06aSJoe Hershberger /* Only check every once in a while */ 1357de15a06aSJoe Hershberger if ((cnt & 0xFFFF) < buffered_size && ctrlc()) 1358de15a06aSJoe Hershberger return ERR_ABORTED; 135959829cc1SJean-Christophe PLAGNIOL-VILLARD } 136059829cc1SJean-Christophe PLAGNIOL-VILLARD #else 136159829cc1SJean-Christophe PLAGNIOL-VILLARD while (cnt >= info->portwidth) { 136259829cc1SJean-Christophe PLAGNIOL-VILLARD cword.l = 0; 136359829cc1SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < info->portwidth; i++) { 136459829cc1SJean-Christophe PLAGNIOL-VILLARD flash_add_byte (info, &cword, *src++); 136559829cc1SJean-Christophe PLAGNIOL-VILLARD } 136659829cc1SJean-Christophe PLAGNIOL-VILLARD if ((rc = flash_write_cfiword (info, wp, cword)) != 0) 136759829cc1SJean-Christophe PLAGNIOL-VILLARD return rc; 136859829cc1SJean-Christophe PLAGNIOL-VILLARD wp += info->portwidth; 136959829cc1SJean-Christophe PLAGNIOL-VILLARD cnt -= info->portwidth; 1370f0105727SStefan Roese FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth); 1371de15a06aSJoe Hershberger /* Only check every once in a while */ 1372de15a06aSJoe Hershberger if ((cnt & 0xFFFF) < info->portwidth && ctrlc()) 1373de15a06aSJoe Hershberger return ERR_ABORTED; 137459829cc1SJean-Christophe PLAGNIOL-VILLARD } 13756d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */ 13769a042e9cSJerry Van Baren 137759829cc1SJean-Christophe PLAGNIOL-VILLARD if (cnt == 0) { 137859829cc1SJean-Christophe PLAGNIOL-VILLARD return (0); 137959829cc1SJean-Christophe PLAGNIOL-VILLARD } 138059829cc1SJean-Christophe PLAGNIOL-VILLARD 138159829cc1SJean-Christophe PLAGNIOL-VILLARD /* 138259829cc1SJean-Christophe PLAGNIOL-VILLARD * handle unaligned tail bytes 138359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 138459829cc1SJean-Christophe PLAGNIOL-VILLARD cword.l = 0; 138509ce9921SBecky Bruce p = (uchar *)wp; 138612d30aa7SHaavard Skinnemoen for (i = 0; (i < info->portwidth) && (cnt > 0); ++i) { 138759829cc1SJean-Christophe PLAGNIOL-VILLARD flash_add_byte (info, &cword, *src++); 138859829cc1SJean-Christophe PLAGNIOL-VILLARD --cnt; 138959829cc1SJean-Christophe PLAGNIOL-VILLARD } 139012d30aa7SHaavard Skinnemoen for (; i < info->portwidth; ++i) 139112d30aa7SHaavard Skinnemoen flash_add_byte (info, &cword, flash_read8(p + i)); 139259829cc1SJean-Christophe PLAGNIOL-VILLARD 139359829cc1SJean-Christophe PLAGNIOL-VILLARD return flash_write_cfiword (info, wp, cword); 139459829cc1SJean-Christophe PLAGNIOL-VILLARD } 139559829cc1SJean-Christophe PLAGNIOL-VILLARD 139620043a4cSStefan Roese static inline int manufact_match(flash_info_t *info, u32 manu) 139720043a4cSStefan Roese { 139820043a4cSStefan Roese return info->manufacturer_id == ((manu & FLASH_VENDMASK) >> 16); 139920043a4cSStefan Roese } 140020043a4cSStefan Roese 140159829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 140259829cc1SJean-Christophe PLAGNIOL-VILLARD */ 14036d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_FLASH_PROTECTION 140459829cc1SJean-Christophe PLAGNIOL-VILLARD 140581316a90SHolger Brunck static int cfi_protect_bugfix(flash_info_t *info, long sector, int prot) 140659829cc1SJean-Christophe PLAGNIOL-VILLARD { 140720043a4cSStefan Roese if (manufact_match(info, INTEL_MANUFACT) 140811dc4010SKim Phillips && info->device_id == NUMONYX_256MBIT) { 140954652991SPhilippe De Muyter /* 141054652991SPhilippe De Muyter * see errata called 141154652991SPhilippe De Muyter * "Numonyx Axcell P33/P30 Specification Update" :) 141254652991SPhilippe De Muyter */ 141354652991SPhilippe De Muyter flash_write_cmd(info, sector, 0, FLASH_CMD_READ_ID); 141454652991SPhilippe De Muyter if (!flash_isequal(info, sector, FLASH_OFFSET_PROTECT, 141554652991SPhilippe De Muyter prot)) { 141654652991SPhilippe De Muyter /* 141754652991SPhilippe De Muyter * cmd must come before FLASH_CMD_PROTECT + 20us 141854652991SPhilippe De Muyter * Disable interrupts which might cause a timeout here. 141954652991SPhilippe De Muyter */ 142054652991SPhilippe De Muyter int flag = disable_interrupts(); 142154652991SPhilippe De Muyter unsigned short cmd; 142254652991SPhilippe De Muyter 142359829cc1SJean-Christophe PLAGNIOL-VILLARD if (prot) 142454652991SPhilippe De Muyter cmd = FLASH_CMD_PROTECT_SET; 142559829cc1SJean-Christophe PLAGNIOL-VILLARD else 142654652991SPhilippe De Muyter cmd = FLASH_CMD_PROTECT_CLEAR; 1427bc9019e1SRafael Campos flash_write_cmd(info, sector, 0, 142854652991SPhilippe De Muyter FLASH_CMD_PROTECT); 142954652991SPhilippe De Muyter flash_write_cmd(info, sector, 0, cmd); 143054652991SPhilippe De Muyter /* re-enable interrupts if necessary */ 143154652991SPhilippe De Muyter if (flag) 143254652991SPhilippe De Muyter enable_interrupts(); 143354652991SPhilippe De Muyter } 143481316a90SHolger Brunck return 1; 143581316a90SHolger Brunck } 143681316a90SHolger Brunck return 0; 143781316a90SHolger Brunck } 143881316a90SHolger Brunck 143981316a90SHolger Brunck int flash_real_protect (flash_info_t * info, long sector, int prot) 144081316a90SHolger Brunck { 144181316a90SHolger Brunck int retcode = 0; 144281316a90SHolger Brunck 144381316a90SHolger Brunck switch (info->vendor) { 144481316a90SHolger Brunck case CFI_CMDSET_INTEL_PROG_REGIONS: 144581316a90SHolger Brunck case CFI_CMDSET_INTEL_STANDARD: 144681316a90SHolger Brunck case CFI_CMDSET_INTEL_EXTENDED: 144781316a90SHolger Brunck if (!cfi_protect_bugfix(info, sector, prot)) { 144881316a90SHolger Brunck flash_write_cmd(info, sector, 0, 144981316a90SHolger Brunck FLASH_CMD_CLEAR_STATUS); 145081316a90SHolger Brunck flash_write_cmd(info, sector, 0, 145181316a90SHolger Brunck FLASH_CMD_PROTECT); 145281316a90SHolger Brunck if (prot) 145381316a90SHolger Brunck flash_write_cmd(info, sector, 0, 145481316a90SHolger Brunck FLASH_CMD_PROTECT_SET); 145581316a90SHolger Brunck else 145681316a90SHolger Brunck flash_write_cmd(info, sector, 0, 145781316a90SHolger Brunck FLASH_CMD_PROTECT_CLEAR); 145881316a90SHolger Brunck 145981316a90SHolger Brunck } 1460bc9019e1SRafael Campos break; 1461bc9019e1SRafael Campos case CFI_CMDSET_AMD_EXTENDED: 1462bc9019e1SRafael Campos case CFI_CMDSET_AMD_STANDARD: 1463bc9019e1SRafael Campos /* U-Boot only checks the first byte */ 146420043a4cSStefan Roese if (manufact_match(info, ATM_MANUFACT)) { 1465bc9019e1SRafael Campos if (prot) { 1466bc9019e1SRafael Campos flash_unlock_seq (info, 0); 1467bc9019e1SRafael Campos flash_write_cmd (info, 0, 1468bc9019e1SRafael Campos info->addr_unlock1, 1469bc9019e1SRafael Campos ATM_CMD_SOFTLOCK_START); 1470bc9019e1SRafael Campos flash_unlock_seq (info, 0); 1471bc9019e1SRafael Campos flash_write_cmd (info, sector, 0, 1472bc9019e1SRafael Campos ATM_CMD_LOCK_SECT); 1473bc9019e1SRafael Campos } else { 1474bc9019e1SRafael Campos flash_write_cmd (info, 0, 1475bc9019e1SRafael Campos info->addr_unlock1, 1476bc9019e1SRafael Campos AMD_CMD_UNLOCK_START); 1477bc9019e1SRafael Campos if (info->device_id == ATM_ID_BV6416) 1478bc9019e1SRafael Campos flash_write_cmd (info, sector, 1479bc9019e1SRafael Campos 0, ATM_CMD_UNLOCK_SECT); 1480bc9019e1SRafael Campos } 1481bc9019e1SRafael Campos } 1482ac6b9115SStefan Roese if (info->legacy_unlock) { 148366863b05SAnatolij Gustschin int flag = disable_interrupts(); 148466863b05SAnatolij Gustschin int lock_flag; 148566863b05SAnatolij Gustschin 148666863b05SAnatolij Gustschin flash_unlock_seq(info, 0); 148766863b05SAnatolij Gustschin flash_write_cmd(info, 0, info->addr_unlock1, 148866863b05SAnatolij Gustschin AMD_CMD_SET_PPB_ENTRY); 148966863b05SAnatolij Gustschin lock_flag = flash_isset(info, sector, 0, 0x01); 149066863b05SAnatolij Gustschin if (prot) { 149166863b05SAnatolij Gustschin if (lock_flag) { 149266863b05SAnatolij Gustschin flash_write_cmd(info, sector, 0, 149366863b05SAnatolij Gustschin AMD_CMD_PPB_LOCK_BC1); 149466863b05SAnatolij Gustschin flash_write_cmd(info, sector, 0, 149566863b05SAnatolij Gustschin AMD_CMD_PPB_LOCK_BC2); 149666863b05SAnatolij Gustschin } 149766863b05SAnatolij Gustschin debug("sector %ld %slocked\n", sector, 149866863b05SAnatolij Gustschin lock_flag ? "" : "already "); 149966863b05SAnatolij Gustschin } else { 150066863b05SAnatolij Gustschin if (!lock_flag) { 150166863b05SAnatolij Gustschin debug("unlock %ld\n", sector); 150266863b05SAnatolij Gustschin flash_write_cmd(info, 0, 0, 150366863b05SAnatolij Gustschin AMD_CMD_PPB_UNLOCK_BC1); 150466863b05SAnatolij Gustschin flash_write_cmd(info, 0, 0, 150566863b05SAnatolij Gustschin AMD_CMD_PPB_UNLOCK_BC2); 150666863b05SAnatolij Gustschin } 150766863b05SAnatolij Gustschin debug("sector %ld %sunlocked\n", sector, 150866863b05SAnatolij Gustschin !lock_flag ? "" : "already "); 150966863b05SAnatolij Gustschin } 151066863b05SAnatolij Gustschin if (flag) 151166863b05SAnatolij Gustschin enable_interrupts(); 151266863b05SAnatolij Gustschin 151366863b05SAnatolij Gustschin if (flash_status_check(info, sector, 151466863b05SAnatolij Gustschin info->erase_blk_tout, 151566863b05SAnatolij Gustschin prot ? "protect" : "unprotect")) 151666863b05SAnatolij Gustschin printf("status check error\n"); 151766863b05SAnatolij Gustschin 151866863b05SAnatolij Gustschin flash_write_cmd(info, 0, 0, 151966863b05SAnatolij Gustschin AMD_CMD_SET_PPB_EXIT_BC1); 152066863b05SAnatolij Gustschin flash_write_cmd(info, 0, 0, 152166863b05SAnatolij Gustschin AMD_CMD_SET_PPB_EXIT_BC2); 152266863b05SAnatolij Gustschin } 1523bc9019e1SRafael Campos break; 15244e00acdeSTsiChung Liew #ifdef CONFIG_FLASH_CFI_LEGACY 15254e00acdeSTsiChung Liew case CFI_CMDSET_AMD_LEGACY: 15264e00acdeSTsiChung Liew flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); 15274e00acdeSTsiChung Liew flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT); 15284e00acdeSTsiChung Liew if (prot) 15294e00acdeSTsiChung Liew flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_SET); 15304e00acdeSTsiChung Liew else 15314e00acdeSTsiChung Liew flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_CLEAR); 15324e00acdeSTsiChung Liew #endif 1533bc9019e1SRafael Campos }; 153459829cc1SJean-Christophe PLAGNIOL-VILLARD 1535df4e813bSStefan Roese /* 1536df4e813bSStefan Roese * Flash needs to be in status register read mode for 1537df4e813bSStefan Roese * flash_full_status_check() to work correctly 1538df4e813bSStefan Roese */ 1539df4e813bSStefan Roese flash_write_cmd(info, sector, 0, FLASH_CMD_READ_STATUS); 154059829cc1SJean-Christophe PLAGNIOL-VILLARD if ((retcode = 154159829cc1SJean-Christophe PLAGNIOL-VILLARD flash_full_status_check (info, sector, info->erase_blk_tout, 154259829cc1SJean-Christophe PLAGNIOL-VILLARD prot ? "protect" : "unprotect")) == 0) { 154359829cc1SJean-Christophe PLAGNIOL-VILLARD 154459829cc1SJean-Christophe PLAGNIOL-VILLARD info->protect[sector] = prot; 154559829cc1SJean-Christophe PLAGNIOL-VILLARD 154659829cc1SJean-Christophe PLAGNIOL-VILLARD /* 154759829cc1SJean-Christophe PLAGNIOL-VILLARD * On some of Intel's flash chips (marked via legacy_unlock) 154859829cc1SJean-Christophe PLAGNIOL-VILLARD * unprotect unprotects all locking. 154959829cc1SJean-Christophe PLAGNIOL-VILLARD */ 155059829cc1SJean-Christophe PLAGNIOL-VILLARD if ((prot == 0) && (info->legacy_unlock)) { 155159829cc1SJean-Christophe PLAGNIOL-VILLARD flash_sect_t i; 155259829cc1SJean-Christophe PLAGNIOL-VILLARD 155359829cc1SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < info->sector_count; i++) { 155459829cc1SJean-Christophe PLAGNIOL-VILLARD if (info->protect[i]) 155559829cc1SJean-Christophe PLAGNIOL-VILLARD flash_real_protect (info, i, 1); 155659829cc1SJean-Christophe PLAGNIOL-VILLARD } 155759829cc1SJean-Christophe PLAGNIOL-VILLARD } 155859829cc1SJean-Christophe PLAGNIOL-VILLARD } 155959829cc1SJean-Christophe PLAGNIOL-VILLARD return retcode; 156059829cc1SJean-Christophe PLAGNIOL-VILLARD } 156159829cc1SJean-Christophe PLAGNIOL-VILLARD 156259829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 156359829cc1SJean-Christophe PLAGNIOL-VILLARD * flash_read_user_serial - read the OneTimeProgramming cells 156459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 156559829cc1SJean-Christophe PLAGNIOL-VILLARD void flash_read_user_serial (flash_info_t * info, void *buffer, int offset, 156659829cc1SJean-Christophe PLAGNIOL-VILLARD int len) 156759829cc1SJean-Christophe PLAGNIOL-VILLARD { 156859829cc1SJean-Christophe PLAGNIOL-VILLARD uchar *src; 156959829cc1SJean-Christophe PLAGNIOL-VILLARD uchar *dst; 157059829cc1SJean-Christophe PLAGNIOL-VILLARD 157159829cc1SJean-Christophe PLAGNIOL-VILLARD dst = buffer; 157212d30aa7SHaavard Skinnemoen src = flash_map (info, 0, FLASH_OFFSET_USER_PROTECTION); 157359829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); 157459829cc1SJean-Christophe PLAGNIOL-VILLARD memcpy (dst, src + offset, len); 157559829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd (info, 0, 0, info->cmd_reset); 1576a90b9575SAaron Williams udelay(1); 157712d30aa7SHaavard Skinnemoen flash_unmap(info, 0, FLASH_OFFSET_USER_PROTECTION, src); 157859829cc1SJean-Christophe PLAGNIOL-VILLARD } 157959829cc1SJean-Christophe PLAGNIOL-VILLARD 158059829cc1SJean-Christophe PLAGNIOL-VILLARD /* 158159829cc1SJean-Christophe PLAGNIOL-VILLARD * flash_read_factory_serial - read the device Id from the protection area 158259829cc1SJean-Christophe PLAGNIOL-VILLARD */ 158359829cc1SJean-Christophe PLAGNIOL-VILLARD void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset, 158459829cc1SJean-Christophe PLAGNIOL-VILLARD int len) 158559829cc1SJean-Christophe PLAGNIOL-VILLARD { 158659829cc1SJean-Christophe PLAGNIOL-VILLARD uchar *src; 158759829cc1SJean-Christophe PLAGNIOL-VILLARD 158812d30aa7SHaavard Skinnemoen src = flash_map (info, 0, FLASH_OFFSET_INTEL_PROTECTION); 158959829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); 159059829cc1SJean-Christophe PLAGNIOL-VILLARD memcpy (buffer, src + offset, len); 159159829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd (info, 0, 0, info->cmd_reset); 1592a90b9575SAaron Williams udelay(1); 159312d30aa7SHaavard Skinnemoen flash_unmap(info, 0, FLASH_OFFSET_INTEL_PROTECTION, src); 159459829cc1SJean-Christophe PLAGNIOL-VILLARD } 159559829cc1SJean-Christophe PLAGNIOL-VILLARD 15966d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_FLASH_PROTECTION */ 159759829cc1SJean-Christophe PLAGNIOL-VILLARD 15980ddf06ddSHaavard Skinnemoen /*----------------------------------------------------------------------- 15990ddf06ddSHaavard Skinnemoen * Reverse the order of the erase regions in the CFI QRY structure. 16000ddf06ddSHaavard Skinnemoen * This is needed for chips that are either a) correctly detected as 16010ddf06ddSHaavard Skinnemoen * top-boot, or b) buggy. 16020ddf06ddSHaavard Skinnemoen */ 16030ddf06ddSHaavard Skinnemoen static void cfi_reverse_geometry(struct cfi_qry *qry) 16040ddf06ddSHaavard Skinnemoen { 16050ddf06ddSHaavard Skinnemoen unsigned int i, j; 16060ddf06ddSHaavard Skinnemoen u32 tmp; 16070ddf06ddSHaavard Skinnemoen 16080ddf06ddSHaavard Skinnemoen for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) { 1609aedadf10SAndrew Gabbasov tmp = get_unaligned(&(qry->erase_region_info[i])); 1610aedadf10SAndrew Gabbasov put_unaligned(get_unaligned(&(qry->erase_region_info[j])), 1611aedadf10SAndrew Gabbasov &(qry->erase_region_info[i])); 1612aedadf10SAndrew Gabbasov put_unaligned(tmp, &(qry->erase_region_info[j])); 16130ddf06ddSHaavard Skinnemoen } 16140ddf06ddSHaavard Skinnemoen } 161559829cc1SJean-Christophe PLAGNIOL-VILLARD 161659829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 161759829cc1SJean-Christophe PLAGNIOL-VILLARD * read jedec ids from device and set corresponding fields in info struct 161859829cc1SJean-Christophe PLAGNIOL-VILLARD * 161959829cc1SJean-Christophe PLAGNIOL-VILLARD * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct 162059829cc1SJean-Christophe PLAGNIOL-VILLARD * 162159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 16220ddf06ddSHaavard Skinnemoen static void cmdset_intel_read_jedec_ids(flash_info_t *info) 162359829cc1SJean-Christophe PLAGNIOL-VILLARD { 162459829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); 1625a90b9575SAaron Williams udelay(1); 162659829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID); 162759829cc1SJean-Christophe PLAGNIOL-VILLARD udelay(1000); /* some flash are slow to respond */ 162859829cc1SJean-Christophe PLAGNIOL-VILLARD info->manufacturer_id = flash_read_uchar (info, 162959829cc1SJean-Christophe PLAGNIOL-VILLARD FLASH_OFFSET_MANUFACTURER_ID); 1630d77c7ac4SPhilippe De Muyter info->device_id = (info->chipwidth == FLASH_CFI_16BIT) ? 1631d77c7ac4SPhilippe De Muyter flash_read_word (info, FLASH_OFFSET_DEVICE_ID) : 1632d77c7ac4SPhilippe De Muyter flash_read_uchar (info, FLASH_OFFSET_DEVICE_ID); 163359829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); 16340ddf06ddSHaavard Skinnemoen } 16350ddf06ddSHaavard Skinnemoen 16360ddf06ddSHaavard Skinnemoen static int cmdset_intel_init(flash_info_t *info, struct cfi_qry *qry) 16370ddf06ddSHaavard Skinnemoen { 16380ddf06ddSHaavard Skinnemoen info->cmd_reset = FLASH_CMD_RESET; 16390ddf06ddSHaavard Skinnemoen 16400ddf06ddSHaavard Skinnemoen cmdset_intel_read_jedec_ids(info); 16410ddf06ddSHaavard Skinnemoen flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI); 16420ddf06ddSHaavard Skinnemoen 16436d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_FLASH_PROTECTION 16440ddf06ddSHaavard Skinnemoen /* read legacy lock/unlock bit from intel flash */ 16450ddf06ddSHaavard Skinnemoen if (info->ext_addr) { 16460ddf06ddSHaavard Skinnemoen info->legacy_unlock = flash_read_uchar (info, 16470ddf06ddSHaavard Skinnemoen info->ext_addr + 5) & 0x08; 16480ddf06ddSHaavard Skinnemoen } 16490ddf06ddSHaavard Skinnemoen #endif 16500ddf06ddSHaavard Skinnemoen 16510ddf06ddSHaavard Skinnemoen return 0; 16520ddf06ddSHaavard Skinnemoen } 16530ddf06ddSHaavard Skinnemoen 16540ddf06ddSHaavard Skinnemoen static void cmdset_amd_read_jedec_ids(flash_info_t *info) 16550ddf06ddSHaavard Skinnemoen { 16563a7b2c21SNiklaus Giger ushort bankId = 0; 16573a7b2c21SNiklaus Giger uchar manuId; 16583a7b2c21SNiklaus Giger 165959829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd(info, 0, 0, AMD_CMD_RESET); 166059829cc1SJean-Christophe PLAGNIOL-VILLARD flash_unlock_seq(info, 0); 166181b20cccSMichael Schwingen flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID); 166259829cc1SJean-Christophe PLAGNIOL-VILLARD udelay(1000); /* some flash are slow to respond */ 166390447ecbSTor Krill 16643a7b2c21SNiklaus Giger manuId = flash_read_uchar (info, FLASH_OFFSET_MANUFACTURER_ID); 16653a7b2c21SNiklaus Giger /* JEDEC JEP106Z specifies ID codes up to bank 7 */ 16663a7b2c21SNiklaus Giger while (manuId == FLASH_CONTINUATION_CODE && bankId < 0x800) { 16673a7b2c21SNiklaus Giger bankId += 0x100; 16683a7b2c21SNiklaus Giger manuId = flash_read_uchar (info, 16693a7b2c21SNiklaus Giger bankId | FLASH_OFFSET_MANUFACTURER_ID); 16703a7b2c21SNiklaus Giger } 16713a7b2c21SNiklaus Giger info->manufacturer_id = manuId; 167290447ecbSTor Krill 167390447ecbSTor Krill switch (info->chipwidth){ 167490447ecbSTor Krill case FLASH_CFI_8BIT: 167559829cc1SJean-Christophe PLAGNIOL-VILLARD info->device_id = flash_read_uchar (info, 167659829cc1SJean-Christophe PLAGNIOL-VILLARD FLASH_OFFSET_DEVICE_ID); 167759829cc1SJean-Christophe PLAGNIOL-VILLARD if (info->device_id == 0x7E) { 167859829cc1SJean-Christophe PLAGNIOL-VILLARD /* AMD 3-byte (expanded) device ids */ 167959829cc1SJean-Christophe PLAGNIOL-VILLARD info->device_id2 = flash_read_uchar (info, 168059829cc1SJean-Christophe PLAGNIOL-VILLARD FLASH_OFFSET_DEVICE_ID2); 168159829cc1SJean-Christophe PLAGNIOL-VILLARD info->device_id2 <<= 8; 168259829cc1SJean-Christophe PLAGNIOL-VILLARD info->device_id2 |= flash_read_uchar (info, 168359829cc1SJean-Christophe PLAGNIOL-VILLARD FLASH_OFFSET_DEVICE_ID3); 168459829cc1SJean-Christophe PLAGNIOL-VILLARD } 168590447ecbSTor Krill break; 168690447ecbSTor Krill case FLASH_CFI_16BIT: 168790447ecbSTor Krill info->device_id = flash_read_word (info, 168890447ecbSTor Krill FLASH_OFFSET_DEVICE_ID); 16895b448adbSHeiko Schocher if ((info->device_id & 0xff) == 0x7E) { 16905b448adbSHeiko Schocher /* AMD 3-byte (expanded) device ids */ 16915b448adbSHeiko Schocher info->device_id2 = flash_read_uchar (info, 16925b448adbSHeiko Schocher FLASH_OFFSET_DEVICE_ID2); 16935b448adbSHeiko Schocher info->device_id2 <<= 8; 16945b448adbSHeiko Schocher info->device_id2 |= flash_read_uchar (info, 16955b448adbSHeiko Schocher FLASH_OFFSET_DEVICE_ID3); 16965b448adbSHeiko Schocher } 169790447ecbSTor Krill break; 169890447ecbSTor Krill default: 169990447ecbSTor Krill break; 170090447ecbSTor Krill } 170159829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd(info, 0, 0, AMD_CMD_RESET); 1702a90b9575SAaron Williams udelay(1); 17030ddf06ddSHaavard Skinnemoen } 17040ddf06ddSHaavard Skinnemoen 17050ddf06ddSHaavard Skinnemoen static int cmdset_amd_init(flash_info_t *info, struct cfi_qry *qry) 17060ddf06ddSHaavard Skinnemoen { 17070ddf06ddSHaavard Skinnemoen info->cmd_reset = AMD_CMD_RESET; 170807b2c5c0SAngelo Dureghello info->cmd_erase_sector = AMD_CMD_ERASE_SECTOR; 17090ddf06ddSHaavard Skinnemoen 17100ddf06ddSHaavard Skinnemoen cmdset_amd_read_jedec_ids(info); 17110ddf06ddSHaavard Skinnemoen flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI); 17120ddf06ddSHaavard Skinnemoen 171366863b05SAnatolij Gustschin #ifdef CONFIG_SYS_FLASH_PROTECTION 1714ac6b9115SStefan Roese if (info->ext_addr) { 1715ac6b9115SStefan Roese /* read sector protect/unprotect scheme (at 0x49) */ 1716ac6b9115SStefan Roese if (flash_read_uchar(info, info->ext_addr + 9) == 0x8) 171766863b05SAnatolij Gustschin info->legacy_unlock = 1; 171866863b05SAnatolij Gustschin } 171966863b05SAnatolij Gustschin #endif 172066863b05SAnatolij Gustschin 17210ddf06ddSHaavard Skinnemoen return 0; 17220ddf06ddSHaavard Skinnemoen } 17230ddf06ddSHaavard Skinnemoen 17240ddf06ddSHaavard Skinnemoen #ifdef CONFIG_FLASH_CFI_LEGACY 17250ddf06ddSHaavard Skinnemoen static void flash_read_jedec_ids (flash_info_t * info) 17260ddf06ddSHaavard Skinnemoen { 17270ddf06ddSHaavard Skinnemoen info->manufacturer_id = 0; 17280ddf06ddSHaavard Skinnemoen info->device_id = 0; 17290ddf06ddSHaavard Skinnemoen info->device_id2 = 0; 17300ddf06ddSHaavard Skinnemoen 17310ddf06ddSHaavard Skinnemoen switch (info->vendor) { 17329c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 17330ddf06ddSHaavard Skinnemoen case CFI_CMDSET_INTEL_STANDARD: 17340ddf06ddSHaavard Skinnemoen case CFI_CMDSET_INTEL_EXTENDED: 17358225d1e3SMichael Schwingen cmdset_intel_read_jedec_ids(info); 17360ddf06ddSHaavard Skinnemoen break; 17370ddf06ddSHaavard Skinnemoen case CFI_CMDSET_AMD_STANDARD: 17380ddf06ddSHaavard Skinnemoen case CFI_CMDSET_AMD_EXTENDED: 17398225d1e3SMichael Schwingen cmdset_amd_read_jedec_ids(info); 174059829cc1SJean-Christophe PLAGNIOL-VILLARD break; 174159829cc1SJean-Christophe PLAGNIOL-VILLARD default: 174259829cc1SJean-Christophe PLAGNIOL-VILLARD break; 174359829cc1SJean-Christophe PLAGNIOL-VILLARD } 174459829cc1SJean-Christophe PLAGNIOL-VILLARD } 174559829cc1SJean-Christophe PLAGNIOL-VILLARD 1746be60a902SHaavard Skinnemoen /*----------------------------------------------------------------------- 1747be60a902SHaavard Skinnemoen * Call board code to request info about non-CFI flash. 1748be60a902SHaavard Skinnemoen * board_flash_get_legacy needs to fill in at least: 1749be60a902SHaavard Skinnemoen * info->portwidth, info->chipwidth and info->interface for Jedec probing. 1750be60a902SHaavard Skinnemoen */ 175109ce9921SBecky Bruce static int flash_detect_legacy(phys_addr_t base, int banknum) 1752be60a902SHaavard Skinnemoen { 1753be60a902SHaavard Skinnemoen flash_info_t *info = &flash_info[banknum]; 1754be60a902SHaavard Skinnemoen 1755be60a902SHaavard Skinnemoen if (board_flash_get_legacy(base, banknum, info)) { 1756be60a902SHaavard Skinnemoen /* board code may have filled info completely. If not, we 1757be60a902SHaavard Skinnemoen use JEDEC ID probing. */ 1758be60a902SHaavard Skinnemoen if (!info->vendor) { 1759be60a902SHaavard Skinnemoen int modes[] = { 1760be60a902SHaavard Skinnemoen CFI_CMDSET_AMD_STANDARD, 1761be60a902SHaavard Skinnemoen CFI_CMDSET_INTEL_STANDARD 1762be60a902SHaavard Skinnemoen }; 1763be60a902SHaavard Skinnemoen int i; 1764be60a902SHaavard Skinnemoen 176531bf0f57SAxel Lin for (i = 0; i < ARRAY_SIZE(modes); i++) { 1766be60a902SHaavard Skinnemoen info->vendor = modes[i]; 176709ce9921SBecky Bruce info->start[0] = 176809ce9921SBecky Bruce (ulong)map_physmem(base, 1769e1fb6d0dSStefan Roese info->portwidth, 177009ce9921SBecky Bruce MAP_NOCACHE); 1771be60a902SHaavard Skinnemoen if (info->portwidth == FLASH_CFI_8BIT 1772be60a902SHaavard Skinnemoen && info->interface == FLASH_CFI_X8X16) { 1773be60a902SHaavard Skinnemoen info->addr_unlock1 = 0x2AAA; 1774be60a902SHaavard Skinnemoen info->addr_unlock2 = 0x5555; 1775be60a902SHaavard Skinnemoen } else { 1776be60a902SHaavard Skinnemoen info->addr_unlock1 = 0x5555; 1777be60a902SHaavard Skinnemoen info->addr_unlock2 = 0x2AAA; 1778be60a902SHaavard Skinnemoen } 1779be60a902SHaavard Skinnemoen flash_read_jedec_ids(info); 1780be60a902SHaavard Skinnemoen debug("JEDEC PROBE: ID %x %x %x\n", 1781be60a902SHaavard Skinnemoen info->manufacturer_id, 1782be60a902SHaavard Skinnemoen info->device_id, 1783be60a902SHaavard Skinnemoen info->device_id2); 178409ce9921SBecky Bruce if (jedec_flash_match(info, info->start[0])) 1785be60a902SHaavard Skinnemoen break; 178609ce9921SBecky Bruce else 1787e1fb6d0dSStefan Roese unmap_physmem((void *)info->start[0], 1788d8b57c0aSKuo-Jung Su info->portwidth); 1789be60a902SHaavard Skinnemoen } 1790be60a902SHaavard Skinnemoen } 1791be60a902SHaavard Skinnemoen 1792be60a902SHaavard Skinnemoen switch(info->vendor) { 17939c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 1794be60a902SHaavard Skinnemoen case CFI_CMDSET_INTEL_STANDARD: 1795be60a902SHaavard Skinnemoen case CFI_CMDSET_INTEL_EXTENDED: 1796be60a902SHaavard Skinnemoen info->cmd_reset = FLASH_CMD_RESET; 1797be60a902SHaavard Skinnemoen break; 1798be60a902SHaavard Skinnemoen case CFI_CMDSET_AMD_STANDARD: 1799be60a902SHaavard Skinnemoen case CFI_CMDSET_AMD_EXTENDED: 1800be60a902SHaavard Skinnemoen case CFI_CMDSET_AMD_LEGACY: 1801be60a902SHaavard Skinnemoen info->cmd_reset = AMD_CMD_RESET; 1802be60a902SHaavard Skinnemoen break; 1803be60a902SHaavard Skinnemoen } 1804be60a902SHaavard Skinnemoen info->flash_id = FLASH_MAN_CFI; 1805be60a902SHaavard Skinnemoen return 1; 1806be60a902SHaavard Skinnemoen } 1807be60a902SHaavard Skinnemoen return 0; /* use CFI */ 1808be60a902SHaavard Skinnemoen } 1809be60a902SHaavard Skinnemoen #else 181009ce9921SBecky Bruce static inline int flash_detect_legacy(phys_addr_t base, int banknum) 1811be60a902SHaavard Skinnemoen { 1812be60a902SHaavard Skinnemoen return 0; /* use CFI */ 1813be60a902SHaavard Skinnemoen } 1814be60a902SHaavard Skinnemoen #endif 1815be60a902SHaavard Skinnemoen 181659829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 181759829cc1SJean-Christophe PLAGNIOL-VILLARD * detect if flash is compatible with the Common Flash Interface (CFI) 181859829cc1SJean-Christophe PLAGNIOL-VILLARD * http://www.jedec.org/download/search/jesd68.pdf 181959829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1820e23741f4SHaavard Skinnemoen static void flash_read_cfi (flash_info_t *info, void *buf, 1821e23741f4SHaavard Skinnemoen unsigned int start, size_t len) 1822e23741f4SHaavard Skinnemoen { 1823e23741f4SHaavard Skinnemoen u8 *p = buf; 1824e23741f4SHaavard Skinnemoen unsigned int i; 1825e23741f4SHaavard Skinnemoen 1826e23741f4SHaavard Skinnemoen for (i = 0; i < len; i++) 1827e303be2dSStefan Roese p[i] = flash_read_uchar(info, start + i); 1828e23741f4SHaavard Skinnemoen } 1829e23741f4SHaavard Skinnemoen 183011dc4010SKim Phillips static void __flash_cmd_reset(flash_info_t *info) 1831fa36ae79SStefan Roese { 1832fa36ae79SStefan Roese /* 1833fa36ae79SStefan Roese * We do not yet know what kind of commandset to use, so we issue 1834fa36ae79SStefan Roese * the reset command in both Intel and AMD variants, in the hope 1835fa36ae79SStefan Roese * that AMD flash roms ignore the Intel command. 1836fa36ae79SStefan Roese */ 1837fa36ae79SStefan Roese flash_write_cmd(info, 0, 0, AMD_CMD_RESET); 1838a90b9575SAaron Williams udelay(1); 1839fa36ae79SStefan Roese flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); 1840fa36ae79SStefan Roese } 1841fa36ae79SStefan Roese void flash_cmd_reset(flash_info_t *info) 1842fa36ae79SStefan Roese __attribute__((weak,alias("__flash_cmd_reset"))); 1843fa36ae79SStefan Roese 1844e23741f4SHaavard Skinnemoen static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry) 184559829cc1SJean-Christophe PLAGNIOL-VILLARD { 184659829cc1SJean-Christophe PLAGNIOL-VILLARD int cfi_offset; 184759829cc1SJean-Christophe PLAGNIOL-VILLARD 1848e303be2dSStefan Roese /* Issue FLASH reset command */ 1849e303be2dSStefan Roese flash_cmd_reset(info); 1850e303be2dSStefan Roese 185131bf0f57SAxel Lin for (cfi_offset = 0; cfi_offset < ARRAY_SIZE(flash_offset_cfi); 18527e5b9b47SHaavard Skinnemoen cfi_offset++) { 18537e5b9b47SHaavard Skinnemoen flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset], 18547e5b9b47SHaavard Skinnemoen FLASH_CMD_CFI); 1855e303be2dSStefan Roese if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q') 1856e303be2dSStefan Roese && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R') 1857e303be2dSStefan Roese && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) { 1858e23741f4SHaavard Skinnemoen flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP, 1859e23741f4SHaavard Skinnemoen sizeof(struct cfi_qry)); 1860e23741f4SHaavard Skinnemoen info->interface = le16_to_cpu(qry->interface_desc); 1861e303be2dSStefan Roese 186259829cc1SJean-Christophe PLAGNIOL-VILLARD info->cfi_offset = flash_offset_cfi[cfi_offset]; 186359829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("device interface is %d\n", 186459829cc1SJean-Christophe PLAGNIOL-VILLARD info->interface); 186559829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("found port %d chip %d ", 186659829cc1SJean-Christophe PLAGNIOL-VILLARD info->portwidth, info->chipwidth); 186759829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("port %d bits chip %d bits\n", 186859829cc1SJean-Christophe PLAGNIOL-VILLARD info->portwidth << CFI_FLASH_SHIFT_WIDTH, 186959829cc1SJean-Christophe PLAGNIOL-VILLARD info->chipwidth << CFI_FLASH_SHIFT_WIDTH); 187042026c9cSBartlomiej Sieka 187142026c9cSBartlomiej Sieka /* calculate command offsets as in the Linux driver */ 1872e303be2dSStefan Roese info->addr_unlock1 = 0x555; 1873e303be2dSStefan Roese info->addr_unlock2 = 0x2aa; 187442026c9cSBartlomiej Sieka 187542026c9cSBartlomiej Sieka /* 187642026c9cSBartlomiej Sieka * modify the unlock address if we are 187742026c9cSBartlomiej Sieka * in compatibility mode 187842026c9cSBartlomiej Sieka */ 187942026c9cSBartlomiej Sieka if ( /* x8/x16 in x8 mode */ 188042026c9cSBartlomiej Sieka ((info->chipwidth == FLASH_CFI_BY8) && 188142026c9cSBartlomiej Sieka (info->interface == FLASH_CFI_X8X16)) || 188242026c9cSBartlomiej Sieka /* x16/x32 in x16 mode */ 188342026c9cSBartlomiej Sieka ((info->chipwidth == FLASH_CFI_BY16) && 188442026c9cSBartlomiej Sieka (info->interface == FLASH_CFI_X16X32))) 188542026c9cSBartlomiej Sieka { 188642026c9cSBartlomiej Sieka info->addr_unlock1 = 0xaaa; 188742026c9cSBartlomiej Sieka info->addr_unlock2 = 0x555; 188842026c9cSBartlomiej Sieka } 188942026c9cSBartlomiej Sieka 189081b20cccSMichael Schwingen info->name = "CFI conformant"; 189159829cc1SJean-Christophe PLAGNIOL-VILLARD return 1; 189259829cc1SJean-Christophe PLAGNIOL-VILLARD } 189359829cc1SJean-Christophe PLAGNIOL-VILLARD } 18947e5b9b47SHaavard Skinnemoen 18957e5b9b47SHaavard Skinnemoen return 0; 189659829cc1SJean-Christophe PLAGNIOL-VILLARD } 18977e5b9b47SHaavard Skinnemoen 1898e23741f4SHaavard Skinnemoen static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry) 18997e5b9b47SHaavard Skinnemoen { 19007e5b9b47SHaavard Skinnemoen debug ("flash detect cfi\n"); 19017e5b9b47SHaavard Skinnemoen 19026d0f6bcfSJean-Christophe PLAGNIOL-VILLARD for (info->portwidth = CONFIG_SYS_FLASH_CFI_WIDTH; 19037e5b9b47SHaavard Skinnemoen info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) { 19047e5b9b47SHaavard Skinnemoen for (info->chipwidth = FLASH_CFI_BY8; 19057e5b9b47SHaavard Skinnemoen info->chipwidth <= info->portwidth; 19067e5b9b47SHaavard Skinnemoen info->chipwidth <<= 1) 1907e303be2dSStefan Roese if (__flash_detect_cfi(info, qry)) 19087e5b9b47SHaavard Skinnemoen return 1; 190959829cc1SJean-Christophe PLAGNIOL-VILLARD } 191059829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("not found\n"); 191159829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 191259829cc1SJean-Christophe PLAGNIOL-VILLARD } 191359829cc1SJean-Christophe PLAGNIOL-VILLARD 191459829cc1SJean-Christophe PLAGNIOL-VILLARD /* 1915467bcee1SHaavard Skinnemoen * Manufacturer-specific quirks. Add workarounds for geometry 1916467bcee1SHaavard Skinnemoen * reversal, etc. here. 1917467bcee1SHaavard Skinnemoen */ 1918467bcee1SHaavard Skinnemoen static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry) 1919467bcee1SHaavard Skinnemoen { 1920467bcee1SHaavard Skinnemoen /* check if flash geometry needs reversal */ 1921467bcee1SHaavard Skinnemoen if (qry->num_erase_regions > 1) { 1922467bcee1SHaavard Skinnemoen /* reverse geometry if top boot part */ 1923467bcee1SHaavard Skinnemoen if (info->cfi_version < 0x3131) { 1924467bcee1SHaavard Skinnemoen /* CFI < 1.1, try to guess from device id */ 1925467bcee1SHaavard Skinnemoen if ((info->device_id & 0x80) != 0) 1926467bcee1SHaavard Skinnemoen cfi_reverse_geometry(qry); 1927e303be2dSStefan Roese } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) { 1928467bcee1SHaavard Skinnemoen /* CFI >= 1.1, deduct from top/bottom flag */ 1929467bcee1SHaavard Skinnemoen /* note: ext_addr is valid since cfi_version > 0 */ 1930467bcee1SHaavard Skinnemoen cfi_reverse_geometry(qry); 1931467bcee1SHaavard Skinnemoen } 1932467bcee1SHaavard Skinnemoen } 1933467bcee1SHaavard Skinnemoen } 1934467bcee1SHaavard Skinnemoen 1935467bcee1SHaavard Skinnemoen static void flash_fixup_atmel(flash_info_t *info, struct cfi_qry *qry) 1936467bcee1SHaavard Skinnemoen { 1937467bcee1SHaavard Skinnemoen int reverse_geometry = 0; 1938467bcee1SHaavard Skinnemoen 1939467bcee1SHaavard Skinnemoen /* Check the "top boot" bit in the PRI */ 1940467bcee1SHaavard Skinnemoen if (info->ext_addr && !(flash_read_uchar(info, info->ext_addr + 6) & 1)) 1941467bcee1SHaavard Skinnemoen reverse_geometry = 1; 1942467bcee1SHaavard Skinnemoen 1943467bcee1SHaavard Skinnemoen /* AT49BV6416(T) list the erase regions in the wrong order. 1944467bcee1SHaavard Skinnemoen * However, the device ID is identical with the non-broken 1945cb82a532SUlf Samuelsson * AT49BV642D they differ in the high byte. 1946467bcee1SHaavard Skinnemoen */ 1947467bcee1SHaavard Skinnemoen if (info->device_id == 0xd6 || info->device_id == 0xd2) 1948467bcee1SHaavard Skinnemoen reverse_geometry = !reverse_geometry; 1949467bcee1SHaavard Skinnemoen 1950467bcee1SHaavard Skinnemoen if (reverse_geometry) 1951467bcee1SHaavard Skinnemoen cfi_reverse_geometry(qry); 1952467bcee1SHaavard Skinnemoen } 1953467bcee1SHaavard Skinnemoen 1954e8eac437SRichard Retanubun static void flash_fixup_stm(flash_info_t *info, struct cfi_qry *qry) 1955e8eac437SRichard Retanubun { 1956e8eac437SRichard Retanubun /* check if flash geometry needs reversal */ 1957e8eac437SRichard Retanubun if (qry->num_erase_regions > 1) { 1958e8eac437SRichard Retanubun /* reverse geometry if top boot part */ 1959e8eac437SRichard Retanubun if (info->cfi_version < 0x3131) { 19606a011ce8SMike Frysinger /* CFI < 1.1, guess by device id */ 19616a011ce8SMike Frysinger if (info->device_id == 0x22CA || /* M29W320DT */ 19626a011ce8SMike Frysinger info->device_id == 0x2256 || /* M29W320ET */ 19636a011ce8SMike Frysinger info->device_id == 0x22D7) { /* M29W800DT */ 1964e8eac437SRichard Retanubun cfi_reverse_geometry(qry); 1965e8eac437SRichard Retanubun } 19664c2105cbSMike Frysinger } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) { 19674c2105cbSMike Frysinger /* CFI >= 1.1, deduct from top/bottom flag */ 19684c2105cbSMike Frysinger /* note: ext_addr is valid since cfi_version > 0 */ 19694c2105cbSMike Frysinger cfi_reverse_geometry(qry); 1970e8eac437SRichard Retanubun } 1971e8eac437SRichard Retanubun } 1972e8eac437SRichard Retanubun } 1973e8eac437SRichard Retanubun 197407b2c5c0SAngelo Dureghello static void flash_fixup_sst(flash_info_t *info, struct cfi_qry *qry) 197507b2c5c0SAngelo Dureghello { 197607b2c5c0SAngelo Dureghello /* 197707b2c5c0SAngelo Dureghello * SST, for many recent nor parallel flashes, says they are 197807b2c5c0SAngelo Dureghello * CFI-conformant. This is not true, since qry struct. 197907b2c5c0SAngelo Dureghello * reports a std. AMD command set (0x0002), while SST allows to 198007b2c5c0SAngelo Dureghello * erase two different sector sizes for the same memory. 198107b2c5c0SAngelo Dureghello * 64KB sector (SST call it block) needs 0x30 to be erased. 198207b2c5c0SAngelo Dureghello * 4KB sector (SST call it sector) needs 0x50 to be erased. 198307b2c5c0SAngelo Dureghello * Since CFI query detect the 4KB number of sectors, users expects 198407b2c5c0SAngelo Dureghello * a sector granularity of 4KB, and it is here set. 198507b2c5c0SAngelo Dureghello */ 198607b2c5c0SAngelo Dureghello if (info->device_id == 0x5D23 || /* SST39VF3201B */ 198707b2c5c0SAngelo Dureghello info->device_id == 0x5C23) { /* SST39VF3202B */ 198807b2c5c0SAngelo Dureghello /* set sector granularity to 4KB */ 198907b2c5c0SAngelo Dureghello info->cmd_erase_sector=0x50; 199007b2c5c0SAngelo Dureghello } 199107b2c5c0SAngelo Dureghello } 199207b2c5c0SAngelo Dureghello 1993c502321cSJagannadha Sutradharudu Teki static void flash_fixup_num(flash_info_t *info, struct cfi_qry *qry) 1994c502321cSJagannadha Sutradharudu Teki { 1995c502321cSJagannadha Sutradharudu Teki /* 1996c502321cSJagannadha Sutradharudu Teki * The M29EW devices seem to report the CFI information wrong 1997c502321cSJagannadha Sutradharudu Teki * when it's in 8 bit mode. 1998c502321cSJagannadha Sutradharudu Teki * There's an app note from Numonyx on this issue. 1999c502321cSJagannadha Sutradharudu Teki * So adjust the buffer size for M29EW while operating in 8-bit mode 2000c502321cSJagannadha Sutradharudu Teki */ 2001c502321cSJagannadha Sutradharudu Teki if (((qry->max_buf_write_size) > 0x8) && 2002c502321cSJagannadha Sutradharudu Teki (info->device_id == 0x7E) && 2003c502321cSJagannadha Sutradharudu Teki (info->device_id2 == 0x2201 || 2004c502321cSJagannadha Sutradharudu Teki info->device_id2 == 0x2301 || 2005c502321cSJagannadha Sutradharudu Teki info->device_id2 == 0x2801 || 2006c502321cSJagannadha Sutradharudu Teki info->device_id2 == 0x4801)) { 2007c502321cSJagannadha Sutradharudu Teki debug("Adjusted buffer size on Numonyx flash" 2008c502321cSJagannadha Sutradharudu Teki " M29EW family in 8 bit mode\n"); 2009c502321cSJagannadha Sutradharudu Teki qry->max_buf_write_size = 0x8; 2010c502321cSJagannadha Sutradharudu Teki } 2011c502321cSJagannadha Sutradharudu Teki } 2012c502321cSJagannadha Sutradharudu Teki 2013467bcee1SHaavard Skinnemoen /* 201459829cc1SJean-Christophe PLAGNIOL-VILLARD * The following code cannot be run from FLASH! 201559829cc1SJean-Christophe PLAGNIOL-VILLARD * 201659829cc1SJean-Christophe PLAGNIOL-VILLARD */ 201734bbb8fbSAnatolij Gustschin ulong flash_get_size (phys_addr_t base, int banknum) 201859829cc1SJean-Christophe PLAGNIOL-VILLARD { 201959829cc1SJean-Christophe PLAGNIOL-VILLARD flash_info_t *info = &flash_info[banknum]; 202059829cc1SJean-Christophe PLAGNIOL-VILLARD int i, j; 202159829cc1SJean-Christophe PLAGNIOL-VILLARD flash_sect_t sect_cnt; 202209ce9921SBecky Bruce phys_addr_t sector; 202359829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned long tmp; 202459829cc1SJean-Christophe PLAGNIOL-VILLARD int size_ratio; 202559829cc1SJean-Christophe PLAGNIOL-VILLARD uchar num_erase_regions; 202659829cc1SJean-Christophe PLAGNIOL-VILLARD int erase_region_size; 202759829cc1SJean-Christophe PLAGNIOL-VILLARD int erase_region_count; 2028e23741f4SHaavard Skinnemoen struct cfi_qry qry; 202934bbb8fbSAnatolij Gustschin unsigned long max_size; 203059829cc1SJean-Christophe PLAGNIOL-VILLARD 2031f979690eSKumar Gala memset(&qry, 0, sizeof(qry)); 2032f979690eSKumar Gala 203359829cc1SJean-Christophe PLAGNIOL-VILLARD info->ext_addr = 0; 203459829cc1SJean-Christophe PLAGNIOL-VILLARD info->cfi_version = 0; 20356d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_FLASH_PROTECTION 203659829cc1SJean-Christophe PLAGNIOL-VILLARD info->legacy_unlock = 0; 203759829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 203859829cc1SJean-Christophe PLAGNIOL-VILLARD 203909ce9921SBecky Bruce info->start[0] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE); 204059829cc1SJean-Christophe PLAGNIOL-VILLARD 2041e23741f4SHaavard Skinnemoen if (flash_detect_cfi (info, &qry)) { 2042aedadf10SAndrew Gabbasov info->vendor = le16_to_cpu(get_unaligned(&(qry.p_id))); 2043aedadf10SAndrew Gabbasov info->ext_addr = le16_to_cpu(get_unaligned(&(qry.p_adr))); 2044e23741f4SHaavard Skinnemoen num_erase_regions = qry.num_erase_regions; 2045e23741f4SHaavard Skinnemoen 204659829cc1SJean-Christophe PLAGNIOL-VILLARD if (info->ext_addr) { 204759829cc1SJean-Christophe PLAGNIOL-VILLARD info->cfi_version = (ushort) flash_read_uchar (info, 2048e303be2dSStefan Roese info->ext_addr + 3) << 8; 204959829cc1SJean-Christophe PLAGNIOL-VILLARD info->cfi_version |= (ushort) flash_read_uchar (info, 2050e303be2dSStefan Roese info->ext_addr + 4); 205159829cc1SJean-Christophe PLAGNIOL-VILLARD } 20520ddf06ddSHaavard Skinnemoen 205359829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 2054e23741f4SHaavard Skinnemoen flash_printqry (&qry); 205559829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 20560ddf06ddSHaavard Skinnemoen 205759829cc1SJean-Christophe PLAGNIOL-VILLARD switch (info->vendor) { 20589c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 205959829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_INTEL_STANDARD: 206059829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_INTEL_EXTENDED: 20610ddf06ddSHaavard Skinnemoen cmdset_intel_init(info, &qry); 206259829cc1SJean-Christophe PLAGNIOL-VILLARD break; 206359829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_AMD_STANDARD: 206459829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_AMD_EXTENDED: 20650ddf06ddSHaavard Skinnemoen cmdset_amd_init(info, &qry); 206659829cc1SJean-Christophe PLAGNIOL-VILLARD break; 20670ddf06ddSHaavard Skinnemoen default: 20680ddf06ddSHaavard Skinnemoen printf("CFI: Unknown command set 0x%x\n", 20690ddf06ddSHaavard Skinnemoen info->vendor); 20700ddf06ddSHaavard Skinnemoen /* 20710ddf06ddSHaavard Skinnemoen * Unfortunately, this means we don't know how 20720ddf06ddSHaavard Skinnemoen * to get the chip back to Read mode. Might 20730ddf06ddSHaavard Skinnemoen * as well try an Intel-style reset... 20740ddf06ddSHaavard Skinnemoen */ 20750ddf06ddSHaavard Skinnemoen flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); 20760ddf06ddSHaavard Skinnemoen return 0; 207759829cc1SJean-Christophe PLAGNIOL-VILLARD } 207859829cc1SJean-Christophe PLAGNIOL-VILLARD 2079467bcee1SHaavard Skinnemoen /* Do manufacturer-specific fixups */ 2080467bcee1SHaavard Skinnemoen switch (info->manufacturer_id) { 20812c9f48afSMario Schuknecht case 0x0001: /* AMD */ 20822c9f48afSMario Schuknecht case 0x0037: /* AMIC */ 2083467bcee1SHaavard Skinnemoen flash_fixup_amd(info, &qry); 2084467bcee1SHaavard Skinnemoen break; 2085467bcee1SHaavard Skinnemoen case 0x001f: 2086467bcee1SHaavard Skinnemoen flash_fixup_atmel(info, &qry); 2087467bcee1SHaavard Skinnemoen break; 2088e8eac437SRichard Retanubun case 0x0020: 2089e8eac437SRichard Retanubun flash_fixup_stm(info, &qry); 2090e8eac437SRichard Retanubun break; 209107b2c5c0SAngelo Dureghello case 0x00bf: /* SST */ 209207b2c5c0SAngelo Dureghello flash_fixup_sst(info, &qry); 209307b2c5c0SAngelo Dureghello break; 2094c502321cSJagannadha Sutradharudu Teki case 0x0089: /* Numonyx */ 2095c502321cSJagannadha Sutradharudu Teki flash_fixup_num(info, &qry); 2096c502321cSJagannadha Sutradharudu Teki break; 2097467bcee1SHaavard Skinnemoen } 2098467bcee1SHaavard Skinnemoen 209959829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("manufacturer is %d\n", info->vendor); 210059829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("manufacturer id is 0x%x\n", info->manufacturer_id); 210159829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("device id is 0x%x\n", info->device_id); 210259829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("device id2 is 0x%x\n", info->device_id2); 210359829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("cfi version is 0x%04x\n", info->cfi_version); 210459829cc1SJean-Christophe PLAGNIOL-VILLARD 210559829cc1SJean-Christophe PLAGNIOL-VILLARD size_ratio = info->portwidth / info->chipwidth; 210659829cc1SJean-Christophe PLAGNIOL-VILLARD /* if the chip is x8/x16 reduce the ratio by half */ 210759829cc1SJean-Christophe PLAGNIOL-VILLARD if ((info->interface == FLASH_CFI_X8X16) 210859829cc1SJean-Christophe PLAGNIOL-VILLARD && (info->chipwidth == FLASH_CFI_BY8)) { 210959829cc1SJean-Christophe PLAGNIOL-VILLARD size_ratio >>= 1; 211059829cc1SJean-Christophe PLAGNIOL-VILLARD } 211159829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("size_ratio %d port %d bits chip %d bits\n", 211259829cc1SJean-Christophe PLAGNIOL-VILLARD size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH, 211359829cc1SJean-Christophe PLAGNIOL-VILLARD info->chipwidth << CFI_FLASH_SHIFT_WIDTH); 2114ec50a8e3SIlya Yanok info->size = 1 << qry.dev_size; 2115ec50a8e3SIlya Yanok /* multiply the size by the number of chips */ 2116ec50a8e3SIlya Yanok info->size *= size_ratio; 211734bbb8fbSAnatolij Gustschin max_size = cfi_flash_bank_size(banknum); 2118ec50a8e3SIlya Yanok if (max_size && (info->size > max_size)) { 2119ec50a8e3SIlya Yanok debug("[truncated from %ldMiB]", info->size >> 20); 2120ec50a8e3SIlya Yanok info->size = max_size; 2121ec50a8e3SIlya Yanok } 212259829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("found %d erase regions\n", num_erase_regions); 212359829cc1SJean-Christophe PLAGNIOL-VILLARD sect_cnt = 0; 212459829cc1SJean-Christophe PLAGNIOL-VILLARD sector = base; 212559829cc1SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < num_erase_regions; i++) { 212659829cc1SJean-Christophe PLAGNIOL-VILLARD if (i > NUM_ERASE_REGIONS) { 212759829cc1SJean-Christophe PLAGNIOL-VILLARD printf ("%d erase regions found, only %d used\n", 212859829cc1SJean-Christophe PLAGNIOL-VILLARD num_erase_regions, NUM_ERASE_REGIONS); 212959829cc1SJean-Christophe PLAGNIOL-VILLARD break; 213059829cc1SJean-Christophe PLAGNIOL-VILLARD } 2131e23741f4SHaavard Skinnemoen 2132aedadf10SAndrew Gabbasov tmp = le32_to_cpu(get_unaligned( 2133aedadf10SAndrew Gabbasov &(qry.erase_region_info[i]))); 21340ddf06ddSHaavard Skinnemoen debug("erase region %u: 0x%08lx\n", i, tmp); 2135e23741f4SHaavard Skinnemoen 2136e23741f4SHaavard Skinnemoen erase_region_count = (tmp & 0xffff) + 1; 2137e23741f4SHaavard Skinnemoen tmp >>= 16; 213859829cc1SJean-Christophe PLAGNIOL-VILLARD erase_region_size = 213959829cc1SJean-Christophe PLAGNIOL-VILLARD (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128; 214059829cc1SJean-Christophe PLAGNIOL-VILLARD debug ("erase_region_count = %d erase_region_size = %d\n", 214159829cc1SJean-Christophe PLAGNIOL-VILLARD erase_region_count, erase_region_size); 214259829cc1SJean-Christophe PLAGNIOL-VILLARD for (j = 0; j < erase_region_count; j++) { 2143ec50a8e3SIlya Yanok if (sector - base >= info->size) 2144ec50a8e3SIlya Yanok break; 21456d0f6bcfSJean-Christophe PLAGNIOL-VILLARD if (sect_cnt >= CONFIG_SYS_MAX_FLASH_SECT) { 214681b20cccSMichael Schwingen printf("ERROR: too many flash sectors\n"); 214781b20cccSMichael Schwingen break; 214881b20cccSMichael Schwingen } 214909ce9921SBecky Bruce info->start[sect_cnt] = 215009ce9921SBecky Bruce (ulong)map_physmem(sector, 215109ce9921SBecky Bruce info->portwidth, 215209ce9921SBecky Bruce MAP_NOCACHE); 215359829cc1SJean-Christophe PLAGNIOL-VILLARD sector += (erase_region_size * size_ratio); 215459829cc1SJean-Christophe PLAGNIOL-VILLARD 215559829cc1SJean-Christophe PLAGNIOL-VILLARD /* 21567e5b9b47SHaavard Skinnemoen * Only read protection status from 21577e5b9b47SHaavard Skinnemoen * supported devices (intel...) 215859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 215959829cc1SJean-Christophe PLAGNIOL-VILLARD switch (info->vendor) { 21609c048b52SVasiliy Leoenenko case CFI_CMDSET_INTEL_PROG_REGIONS: 216159829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_INTEL_EXTENDED: 216259829cc1SJean-Christophe PLAGNIOL-VILLARD case CFI_CMDSET_INTEL_STANDARD: 2163df4e813bSStefan Roese /* 2164df4e813bSStefan Roese * Set flash to read-id mode. Otherwise 2165df4e813bSStefan Roese * reading protected status is not 2166df4e813bSStefan Roese * guaranteed. 2167df4e813bSStefan Roese */ 2168df4e813bSStefan Roese flash_write_cmd(info, sect_cnt, 0, 2169df4e813bSStefan Roese FLASH_CMD_READ_ID); 217059829cc1SJean-Christophe PLAGNIOL-VILLARD info->protect[sect_cnt] = 217159829cc1SJean-Christophe PLAGNIOL-VILLARD flash_isset (info, sect_cnt, 217259829cc1SJean-Christophe PLAGNIOL-VILLARD FLASH_OFFSET_PROTECT, 217359829cc1SJean-Christophe PLAGNIOL-VILLARD FLASH_STATUS_PROTECT); 217459829cc1SJean-Christophe PLAGNIOL-VILLARD break; 217503deff43SStefan Roese case CFI_CMDSET_AMD_EXTENDED: 217603deff43SStefan Roese case CFI_CMDSET_AMD_STANDARD: 2177ac6b9115SStefan Roese if (!info->legacy_unlock) { 217803deff43SStefan Roese /* default: not protected */ 217903deff43SStefan Roese info->protect[sect_cnt] = 0; 218003deff43SStefan Roese break; 218103deff43SStefan Roese } 218203deff43SStefan Roese 218303deff43SStefan Roese /* Read protection (PPB) from sector */ 218403deff43SStefan Roese flash_write_cmd(info, 0, 0, 218503deff43SStefan Roese info->cmd_reset); 218603deff43SStefan Roese flash_unlock_seq(info, 0); 218703deff43SStefan Roese flash_write_cmd(info, 0, 218803deff43SStefan Roese info->addr_unlock1, 218903deff43SStefan Roese FLASH_CMD_READ_ID); 219003deff43SStefan Roese info->protect[sect_cnt] = 219103deff43SStefan Roese flash_isset( 219203deff43SStefan Roese info, sect_cnt, 219303deff43SStefan Roese FLASH_OFFSET_PROTECT, 219403deff43SStefan Roese FLASH_STATUS_PROTECT); 219503deff43SStefan Roese break; 219659829cc1SJean-Christophe PLAGNIOL-VILLARD default: 21977e5b9b47SHaavard Skinnemoen /* default: not protected */ 21987e5b9b47SHaavard Skinnemoen info->protect[sect_cnt] = 0; 219959829cc1SJean-Christophe PLAGNIOL-VILLARD } 220059829cc1SJean-Christophe PLAGNIOL-VILLARD 220159829cc1SJean-Christophe PLAGNIOL-VILLARD sect_cnt++; 220259829cc1SJean-Christophe PLAGNIOL-VILLARD } 220359829cc1SJean-Christophe PLAGNIOL-VILLARD } 220459829cc1SJean-Christophe PLAGNIOL-VILLARD 220559829cc1SJean-Christophe PLAGNIOL-VILLARD info->sector_count = sect_cnt; 2206e23741f4SHaavard Skinnemoen info->buffer_size = 1 << le16_to_cpu(qry.max_buf_write_size); 2207e23741f4SHaavard Skinnemoen tmp = 1 << qry.block_erase_timeout_typ; 22087e5b9b47SHaavard Skinnemoen info->erase_blk_tout = tmp * 2209e23741f4SHaavard Skinnemoen (1 << qry.block_erase_timeout_max); 2210e23741f4SHaavard Skinnemoen tmp = (1 << qry.buf_write_timeout_typ) * 2211e23741f4SHaavard Skinnemoen (1 << qry.buf_write_timeout_max); 2212e23741f4SHaavard Skinnemoen 22137e5b9b47SHaavard Skinnemoen /* round up when converting to ms */ 2214e23741f4SHaavard Skinnemoen info->buffer_write_tout = (tmp + 999) / 1000; 2215e23741f4SHaavard Skinnemoen tmp = (1 << qry.word_write_timeout_typ) * 2216e23741f4SHaavard Skinnemoen (1 << qry.word_write_timeout_max); 22177e5b9b47SHaavard Skinnemoen /* round up when converting to ms */ 2218e23741f4SHaavard Skinnemoen info->write_tout = (tmp + 999) / 1000; 221959829cc1SJean-Christophe PLAGNIOL-VILLARD info->flash_id = FLASH_MAN_CFI; 22207e5b9b47SHaavard Skinnemoen if ((info->interface == FLASH_CFI_X8X16) && 22217e5b9b47SHaavard Skinnemoen (info->chipwidth == FLASH_CFI_BY8)) { 22227e5b9b47SHaavard Skinnemoen /* XXX - Need to test on x8/x16 in parallel. */ 22237e5b9b47SHaavard Skinnemoen info->portwidth >>= 1; 222459829cc1SJean-Christophe PLAGNIOL-VILLARD } 222559829cc1SJean-Christophe PLAGNIOL-VILLARD 222659829cc1SJean-Christophe PLAGNIOL-VILLARD flash_write_cmd (info, 0, 0, info->cmd_reset); 22272215987eSMike Frysinger } 22282215987eSMike Frysinger 222959829cc1SJean-Christophe PLAGNIOL-VILLARD return (info->size); 223059829cc1SJean-Christophe PLAGNIOL-VILLARD } 223159829cc1SJean-Christophe PLAGNIOL-VILLARD 22324ffeab2cSMike Frysinger #ifdef CONFIG_FLASH_CFI_MTD 22336ea808efSPiotr Ziecik void flash_set_verbose(uint v) 22346ea808efSPiotr Ziecik { 22356ea808efSPiotr Ziecik flash_verbose = v; 22366ea808efSPiotr Ziecik } 22374ffeab2cSMike Frysinger #endif 22386ea808efSPiotr Ziecik 22396f726f95SStefan Roese static void cfi_flash_set_config_reg(u32 base, u16 val) 22406f726f95SStefan Roese { 22416f726f95SStefan Roese #ifdef CONFIG_SYS_CFI_FLASH_CONFIG_REGS 22426f726f95SStefan Roese /* 22436f726f95SStefan Roese * Only set this config register if really defined 22446f726f95SStefan Roese * to a valid value (0xffff is invalid) 22456f726f95SStefan Roese */ 22466f726f95SStefan Roese if (val == 0xffff) 22476f726f95SStefan Roese return; 22486f726f95SStefan Roese 22496f726f95SStefan Roese /* 22506f726f95SStefan Roese * Set configuration register. Data is "encrypted" in the 16 lower 22516f726f95SStefan Roese * address bits. 22526f726f95SStefan Roese */ 22536f726f95SStefan Roese flash_write16(FLASH_CMD_SETUP, (void *)(base + (val << 1))); 22546f726f95SStefan Roese flash_write16(FLASH_CMD_SET_CR_CONFIRM, (void *)(base + (val << 1))); 22556f726f95SStefan Roese 22566f726f95SStefan Roese /* 22576f726f95SStefan Roese * Finally issue reset-command to bring device back to 22586f726f95SStefan Roese * read-array mode 22596f726f95SStefan Roese */ 22606f726f95SStefan Roese flash_write16(FLASH_CMD_RESET, (void *)base); 22616f726f95SStefan Roese #endif 22626f726f95SStefan Roese } 22636f726f95SStefan Roese 226459829cc1SJean-Christophe PLAGNIOL-VILLARD /*----------------------------------------------------------------------- 226559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 22666ee1416eSHeiko Schocher 22676ee1416eSHeiko Schocher void flash_protect_default(void) 22686ee1416eSHeiko Schocher { 22692c51983bSPeter Tyser #if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST) 22702c51983bSPeter Tyser int i; 22712c51983bSPeter Tyser struct apl_s { 22722c51983bSPeter Tyser ulong start; 22732c51983bSPeter Tyser ulong size; 22742c51983bSPeter Tyser } apl[] = CONFIG_SYS_FLASH_AUTOPROTECT_LIST; 22752c51983bSPeter Tyser #endif 22762c51983bSPeter Tyser 22776ee1416eSHeiko Schocher /* Monitor protection ON by default */ 22786ee1416eSHeiko Schocher #if (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE) && \ 22796ee1416eSHeiko Schocher (!defined(CONFIG_MONITOR_IS_IN_RAM)) 22806ee1416eSHeiko Schocher flash_protect(FLAG_PROTECT_SET, 22816ee1416eSHeiko Schocher CONFIG_SYS_MONITOR_BASE, 22826ee1416eSHeiko Schocher CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, 22836ee1416eSHeiko Schocher flash_get_info(CONFIG_SYS_MONITOR_BASE)); 22846ee1416eSHeiko Schocher #endif 22856ee1416eSHeiko Schocher 22866ee1416eSHeiko Schocher /* Environment protection ON by default */ 22876ee1416eSHeiko Schocher #ifdef CONFIG_ENV_IS_IN_FLASH 22886ee1416eSHeiko Schocher flash_protect(FLAG_PROTECT_SET, 22896ee1416eSHeiko Schocher CONFIG_ENV_ADDR, 22906ee1416eSHeiko Schocher CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1, 22916ee1416eSHeiko Schocher flash_get_info(CONFIG_ENV_ADDR)); 22926ee1416eSHeiko Schocher #endif 22936ee1416eSHeiko Schocher 22946ee1416eSHeiko Schocher /* Redundant environment protection ON by default */ 22956ee1416eSHeiko Schocher #ifdef CONFIG_ENV_ADDR_REDUND 22966ee1416eSHeiko Schocher flash_protect(FLAG_PROTECT_SET, 22976ee1416eSHeiko Schocher CONFIG_ENV_ADDR_REDUND, 22986ee1416eSHeiko Schocher CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SECT_SIZE - 1, 22996ee1416eSHeiko Schocher flash_get_info(CONFIG_ENV_ADDR_REDUND)); 23006ee1416eSHeiko Schocher #endif 23016ee1416eSHeiko Schocher 23026ee1416eSHeiko Schocher #if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST) 230331bf0f57SAxel Lin for (i = 0; i < ARRAY_SIZE(apl); i++) { 230431d34143SMarek Vasut debug("autoprotecting from %08lx to %08lx\n", 23056ee1416eSHeiko Schocher apl[i].start, apl[i].start + apl[i].size - 1); 23066ee1416eSHeiko Schocher flash_protect(FLAG_PROTECT_SET, 23076ee1416eSHeiko Schocher apl[i].start, 23086ee1416eSHeiko Schocher apl[i].start + apl[i].size - 1, 23096ee1416eSHeiko Schocher flash_get_info(apl[i].start)); 23106ee1416eSHeiko Schocher } 23116ee1416eSHeiko Schocher #endif 23126ee1416eSHeiko Schocher } 23136ee1416eSHeiko Schocher 2314be60a902SHaavard Skinnemoen unsigned long flash_init (void) 231559829cc1SJean-Christophe PLAGNIOL-VILLARD { 2316be60a902SHaavard Skinnemoen unsigned long size = 0; 2317be60a902SHaavard Skinnemoen int i; 231859829cc1SJean-Christophe PLAGNIOL-VILLARD 23196d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_FLASH_PROTECTION 23203a3baf3eSEric Schumann /* read environment from EEPROM */ 23213a3baf3eSEric Schumann char s[64]; 2322cdb74977SWolfgang Denk getenv_f("unlock", s, sizeof(s)); 232381b20cccSMichael Schwingen #endif 2324be60a902SHaavard Skinnemoen 2325be60a902SHaavard Skinnemoen /* Init: no FLASHes known */ 23266d0f6bcfSJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { 2327be60a902SHaavard Skinnemoen flash_info[i].flash_id = FLASH_UNKNOWN; 2328be60a902SHaavard Skinnemoen 23296f726f95SStefan Roese /* Optionally write flash configuration register */ 23306f726f95SStefan Roese cfi_flash_set_config_reg(cfi_flash_bank_addr(i), 23316f726f95SStefan Roese cfi_flash_config_reg(i)); 23326f726f95SStefan Roese 2333b00e19ccSStefan Roese if (!flash_detect_legacy(cfi_flash_bank_addr(i), i)) 233434bbb8fbSAnatolij Gustschin flash_get_size(cfi_flash_bank_addr(i), i); 2335be60a902SHaavard Skinnemoen size += flash_info[i].size; 2336be60a902SHaavard Skinnemoen if (flash_info[i].flash_id == FLASH_UNKNOWN) { 23376d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifndef CONFIG_SYS_FLASH_QUIET_TEST 2338eddf52b5SPeter Tyser printf ("## Unknown flash on Bank %d " 2339be60a902SHaavard Skinnemoen "- Size = 0x%08lx = %ld MB\n", 2340be60a902SHaavard Skinnemoen i+1, flash_info[i].size, 23410e3fa01aSJohn Schmoller flash_info[i].size >> 20); 23426d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_FLASH_QUIET_TEST */ 234359829cc1SJean-Christophe PLAGNIOL-VILLARD } 23446d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_FLASH_PROTECTION 2345c15df21fSJeroen Hofstee else if (strcmp(s, "yes") == 0) { 2346be60a902SHaavard Skinnemoen /* 2347be60a902SHaavard Skinnemoen * Only the U-Boot image and it's environment 2348be60a902SHaavard Skinnemoen * is protected, all other sectors are 2349be60a902SHaavard Skinnemoen * unprotected (unlocked) if flash hardware 23506d0f6bcfSJean-Christophe PLAGNIOL-VILLARD * protection is used (CONFIG_SYS_FLASH_PROTECTION) 2351be60a902SHaavard Skinnemoen * and the environment variable "unlock" is 2352be60a902SHaavard Skinnemoen * set to "yes". 2353be60a902SHaavard Skinnemoen */ 2354be60a902SHaavard Skinnemoen if (flash_info[i].legacy_unlock) { 2355be60a902SHaavard Skinnemoen int k; 235659829cc1SJean-Christophe PLAGNIOL-VILLARD 2357be60a902SHaavard Skinnemoen /* 2358be60a902SHaavard Skinnemoen * Disable legacy_unlock temporarily, 2359be60a902SHaavard Skinnemoen * since flash_real_protect would 2360be60a902SHaavard Skinnemoen * relock all other sectors again 2361be60a902SHaavard Skinnemoen * otherwise. 2362be60a902SHaavard Skinnemoen */ 2363be60a902SHaavard Skinnemoen flash_info[i].legacy_unlock = 0; 236459829cc1SJean-Christophe PLAGNIOL-VILLARD 2365be60a902SHaavard Skinnemoen /* 2366be60a902SHaavard Skinnemoen * Legacy unlocking (e.g. Intel J3) -> 2367be60a902SHaavard Skinnemoen * unlock only one sector. This will 2368be60a902SHaavard Skinnemoen * unlock all sectors. 2369be60a902SHaavard Skinnemoen */ 2370be60a902SHaavard Skinnemoen flash_real_protect (&flash_info[i], 0, 0); 237159829cc1SJean-Christophe PLAGNIOL-VILLARD 2372be60a902SHaavard Skinnemoen flash_info[i].legacy_unlock = 1; 237359829cc1SJean-Christophe PLAGNIOL-VILLARD 2374be60a902SHaavard Skinnemoen /* 2375be60a902SHaavard Skinnemoen * Manually mark other sectors as 2376be60a902SHaavard Skinnemoen * unlocked (unprotected) 2377be60a902SHaavard Skinnemoen */ 2378be60a902SHaavard Skinnemoen for (k = 1; k < flash_info[i].sector_count; k++) 2379be60a902SHaavard Skinnemoen flash_info[i].protect[k] = 0; 2380be60a902SHaavard Skinnemoen } else { 2381be60a902SHaavard Skinnemoen /* 2382be60a902SHaavard Skinnemoen * No legancy unlocking -> unlock all sectors 2383be60a902SHaavard Skinnemoen */ 2384be60a902SHaavard Skinnemoen flash_protect (FLAG_PROTECT_CLEAR, 2385be60a902SHaavard Skinnemoen flash_info[i].start[0], 2386be60a902SHaavard Skinnemoen flash_info[i].start[0] 2387be60a902SHaavard Skinnemoen + flash_info[i].size - 1, 2388be60a902SHaavard Skinnemoen &flash_info[i]); 238959829cc1SJean-Christophe PLAGNIOL-VILLARD } 239059829cc1SJean-Christophe PLAGNIOL-VILLARD } 23916d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_FLASH_PROTECTION */ 239259829cc1SJean-Christophe PLAGNIOL-VILLARD } 239359829cc1SJean-Christophe PLAGNIOL-VILLARD 23946ee1416eSHeiko Schocher flash_protect_default(); 239591809ed5SPiotr Ziecik #ifdef CONFIG_FLASH_CFI_MTD 239691809ed5SPiotr Ziecik cfi_mtd_init(); 239791809ed5SPiotr Ziecik #endif 239891809ed5SPiotr Ziecik 2399be60a902SHaavard Skinnemoen return (size); 240059829cc1SJean-Christophe PLAGNIOL-VILLARD } 2401