xref: /rk3399_rockchip-uboot/drivers/mtd/cfi_flash.c (revision 97bf85d784fbed485e652eb907589ad0d5cb7262)
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  *
1459829cc1SJean-Christophe PLAGNIOL-VILLARD  * See file CREDITS for list of people who contributed to this
1559829cc1SJean-Christophe PLAGNIOL-VILLARD  * project.
1659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1759829cc1SJean-Christophe PLAGNIOL-VILLARD  * This program is free software; you can redistribute it and/or
1859829cc1SJean-Christophe PLAGNIOL-VILLARD  * modify it under the terms of the GNU General Public License as
1959829cc1SJean-Christophe PLAGNIOL-VILLARD  * published by the Free Software Foundation; either version 2 of
2059829cc1SJean-Christophe PLAGNIOL-VILLARD  * the License, or (at your option) any later version.
2159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
2259829cc1SJean-Christophe PLAGNIOL-VILLARD  * This program is distributed in the hope that it will be useful,
2359829cc1SJean-Christophe PLAGNIOL-VILLARD  * but WITHOUT ANY WARRANTY; without even the implied warranty of
2459829cc1SJean-Christophe PLAGNIOL-VILLARD  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
2559829cc1SJean-Christophe PLAGNIOL-VILLARD  * GNU General Public License for more details.
2659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
2759829cc1SJean-Christophe PLAGNIOL-VILLARD  * You should have received a copy of the GNU General Public License
2859829cc1SJean-Christophe PLAGNIOL-VILLARD  * along with this program; if not, write to the Free Software
2959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
3059829cc1SJean-Christophe PLAGNIOL-VILLARD  * MA 02111-1307 USA
3159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
3259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
3359829cc1SJean-Christophe PLAGNIOL-VILLARD 
3459829cc1SJean-Christophe PLAGNIOL-VILLARD /* The DEBUG define must be before common to enable debugging */
3559829cc1SJean-Christophe PLAGNIOL-VILLARD /* #define DEBUG	*/
3659829cc1SJean-Christophe PLAGNIOL-VILLARD 
3759829cc1SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
3859829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/processor.h>
3959829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h>
4059829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/byteorder.h>
4159829cc1SJean-Christophe PLAGNIOL-VILLARD #include <environment.h>
4259829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef	CFG_FLASH_CFI_DRIVER
4359829cc1SJean-Christophe PLAGNIOL-VILLARD 
4459829cc1SJean-Christophe PLAGNIOL-VILLARD /*
457e5b9b47SHaavard Skinnemoen  * This file implements a Common Flash Interface (CFI) driver for
467e5b9b47SHaavard Skinnemoen  * U-Boot.
477e5b9b47SHaavard Skinnemoen  *
487e5b9b47SHaavard Skinnemoen  * The width of the port and the width of the chips are determined at
497e5b9b47SHaavard Skinnemoen  * initialization.  These widths are used to calculate the address for
507e5b9b47SHaavard Skinnemoen  * access CFI data structures.
5159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
5259829cc1SJean-Christophe PLAGNIOL-VILLARD  * References
5359829cc1SJean-Christophe PLAGNIOL-VILLARD  * JEDEC Standard JESD68 - Common Flash Interface (CFI)
5459829cc1SJean-Christophe PLAGNIOL-VILLARD  * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes
5559829cc1SJean-Christophe PLAGNIOL-VILLARD  * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets
5659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet
5759829cc1SJean-Christophe PLAGNIOL-VILLARD  * AMD CFI Specification, Release 2.0 December 1, 2001
5859829cc1SJean-Christophe PLAGNIOL-VILLARD  * AMD/Spansion Application Note: Migration from Single-byte to Three-byte
5959829cc1SJean-Christophe PLAGNIOL-VILLARD  *   Device IDs, Publication Number 25538 Revision A, November 8, 2001
6059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
617e5b9b47SHaavard Skinnemoen  * Define CFG_WRITE_SWAPPED_DATA, if you have to swap the Bytes between
6259829cc1SJean-Christophe PLAGNIOL-VILLARD  * reading and writing ... (yes there is such a Hardware).
6359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
6459829cc1SJean-Christophe PLAGNIOL-VILLARD 
6559829cc1SJean-Christophe PLAGNIOL-VILLARD #ifndef CFG_FLASH_BANKS_LIST
6659829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFG_FLASH_BANKS_LIST { CFG_FLASH_BASE }
6759829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
6859829cc1SJean-Christophe PLAGNIOL-VILLARD 
6959829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_CFI			0x98
7059829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_READ_ID		0x90
7159829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_RESET			0xff
7259829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_BLOCK_ERASE		0x20
7359829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_ERASE_CONFIRM		0xD0
7459829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_WRITE			0x40
7559829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_PROTECT		0x60
7659829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_PROTECT_SET		0x01
7759829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_PROTECT_CLEAR		0xD0
7859829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_CLEAR_STATUS		0x50
7959829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_WRITE_TO_BUFFER	0xE8
8059829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_CMD_WRITE_BUFFER_CONFIRM	0xD0
8159829cc1SJean-Christophe PLAGNIOL-VILLARD 
8259829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_STATUS_DONE		0x80
8359829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_STATUS_ESS		0x40
8459829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_STATUS_ECLBS		0x20
8559829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_STATUS_PSLBS		0x10
8659829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_STATUS_VPENS		0x08
8759829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_STATUS_PSS		0x04
8859829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_STATUS_DPS		0x02
8959829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_STATUS_R			0x01
9059829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_STATUS_PROTECT		0x01
9159829cc1SJean-Christophe PLAGNIOL-VILLARD 
9259829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_CMD_RESET			0xF0
9359829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_CMD_WRITE			0xA0
9459829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_CMD_ERASE_START		0x80
9559829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_CMD_ERASE_SECTOR		0x30
9659829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_CMD_UNLOCK_START		0xAA
9759829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_CMD_UNLOCK_ACK		0x55
9859829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_CMD_WRITE_TO_BUFFER		0x25
9959829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_CMD_WRITE_BUFFER_CONFIRM	0x29
10059829cc1SJean-Christophe PLAGNIOL-VILLARD 
10159829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_STATUS_TOGGLE		0x40
10259829cc1SJean-Christophe PLAGNIOL-VILLARD #define AMD_STATUS_ERROR		0x20
10359829cc1SJean-Christophe PLAGNIOL-VILLARD 
10459829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_MANUFACTURER_ID	0x00
10559829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_DEVICE_ID		0x01
10659829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_DEVICE_ID2		0x0E
10759829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_DEVICE_ID3		0x0F
10859829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_CFI		0x55
10959829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_CFI_ALT		0x555
11059829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_CFI_RESP		0x10
11159829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_PRIMARY_VENDOR	0x13
1127e5b9b47SHaavard Skinnemoen /* extended query table primary address */
1137e5b9b47SHaavard Skinnemoen #define FLASH_OFFSET_EXT_QUERY_T_P_ADDR	0x15
11459829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_WTOUT		0x1F
11559829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_WBTOUT		0x20
11659829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_ETOUT		0x21
11759829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_CETOUT		0x22
11859829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_WMAX_TOUT		0x23
11959829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_WBMAX_TOUT		0x24
12059829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_EMAX_TOUT		0x25
12159829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_CEMAX_TOUT		0x26
12259829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_SIZE		0x27
12359829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_INTERFACE		0x28
12459829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_BUFFER_SIZE	0x2A
12559829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_NUM_ERASE_REGIONS	0x2C
12659829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_ERASE_REGIONS	0x2D
12759829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_PROTECT		0x02
12859829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_USER_PROTECTION	0x85
12959829cc1SJean-Christophe PLAGNIOL-VILLARD #define FLASH_OFFSET_INTEL_PROTECTION	0x81
13059829cc1SJean-Christophe PLAGNIOL-VILLARD 
13159829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFI_CMDSET_NONE			0
13259829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFI_CMDSET_INTEL_EXTENDED	1
13359829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFI_CMDSET_AMD_STANDARD		2
13459829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFI_CMDSET_INTEL_STANDARD	3
13559829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFI_CMDSET_AMD_EXTENDED		4
13659829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFI_CMDSET_MITSU_STANDARD	256
13759829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFI_CMDSET_MITSU_EXTENDED	257
13859829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFI_CMDSET_SST			258
13959829cc1SJean-Christophe PLAGNIOL-VILLARD 
14059829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CFG_FLASH_CFI_AMD_RESET /* needed for STM_ID_29W320DB on UC100 */
14159829cc1SJean-Christophe PLAGNIOL-VILLARD # undef  FLASH_CMD_RESET
14259829cc1SJean-Christophe PLAGNIOL-VILLARD # define FLASH_CMD_RESET	AMD_CMD_RESET /* use AMD-Reset instead */
14359829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
14459829cc1SJean-Christophe PLAGNIOL-VILLARD 
14559829cc1SJean-Christophe PLAGNIOL-VILLARD typedef union {
14659829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned char c;
14759829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned short w;
14859829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned long l;
14959829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned long long ll;
15059829cc1SJean-Christophe PLAGNIOL-VILLARD } cfiword_t;
15159829cc1SJean-Christophe PLAGNIOL-VILLARD 
15259829cc1SJean-Christophe PLAGNIOL-VILLARD #define NUM_ERASE_REGIONS	4 /* max. number of erase regions */
15359829cc1SJean-Christophe PLAGNIOL-VILLARD 
15459829cc1SJean-Christophe PLAGNIOL-VILLARD static uint flash_offset_cfi[2] = { FLASH_OFFSET_CFI, FLASH_OFFSET_CFI_ALT };
15559829cc1SJean-Christophe PLAGNIOL-VILLARD 
15659829cc1SJean-Christophe PLAGNIOL-VILLARD /* use CFG_MAX_FLASH_BANKS_DETECT if defined */
15759829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CFG_MAX_FLASH_BANKS_DETECT
15859829cc1SJean-Christophe PLAGNIOL-VILLARD static ulong bank_base[CFG_MAX_FLASH_BANKS_DETECT] = CFG_FLASH_BANKS_LIST;
15959829cc1SJean-Christophe PLAGNIOL-VILLARD flash_info_t flash_info[CFG_MAX_FLASH_BANKS_DETECT];	/* FLASH chips info */
16059829cc1SJean-Christophe PLAGNIOL-VILLARD #else
16159829cc1SJean-Christophe PLAGNIOL-VILLARD static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST;
16259829cc1SJean-Christophe PLAGNIOL-VILLARD flash_info_t flash_info[CFG_MAX_FLASH_BANKS];		/* FLASH chips info */
16359829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
16459829cc1SJean-Christophe PLAGNIOL-VILLARD 
16559829cc1SJean-Christophe PLAGNIOL-VILLARD /*
16659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check if chip width is defined. If not, start detecting with 8bit.
16759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
16859829cc1SJean-Christophe PLAGNIOL-VILLARD #ifndef CFG_FLASH_CFI_WIDTH
16959829cc1SJean-Christophe PLAGNIOL-VILLARD #define CFG_FLASH_CFI_WIDTH	FLASH_CFI_8BIT
17059829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
17159829cc1SJean-Christophe PLAGNIOL-VILLARD 
17259829cc1SJean-Christophe PLAGNIOL-VILLARD typedef unsigned long flash_sect_t;
17359829cc1SJean-Christophe PLAGNIOL-VILLARD 
174e23741f4SHaavard Skinnemoen /* CFI standard query structure */
175e23741f4SHaavard Skinnemoen struct cfi_qry {
176e23741f4SHaavard Skinnemoen 	u8	qry[3];
177e23741f4SHaavard Skinnemoen 	u16	p_id;
178e23741f4SHaavard Skinnemoen 	u16	p_adr;
179e23741f4SHaavard Skinnemoen 	u16	a_id;
180e23741f4SHaavard Skinnemoen 	u16	a_adr;
181e23741f4SHaavard Skinnemoen 	u8	vcc_min;
182e23741f4SHaavard Skinnemoen 	u8	vcc_max;
183e23741f4SHaavard Skinnemoen 	u8	vpp_min;
184e23741f4SHaavard Skinnemoen 	u8	vpp_max;
185e23741f4SHaavard Skinnemoen 	u8	word_write_timeout_typ;
186e23741f4SHaavard Skinnemoen 	u8	buf_write_timeout_typ;
187e23741f4SHaavard Skinnemoen 	u8	block_erase_timeout_typ;
188e23741f4SHaavard Skinnemoen 	u8	chip_erase_timeout_typ;
189e23741f4SHaavard Skinnemoen 	u8	word_write_timeout_max;
190e23741f4SHaavard Skinnemoen 	u8	buf_write_timeout_max;
191e23741f4SHaavard Skinnemoen 	u8	block_erase_timeout_max;
192e23741f4SHaavard Skinnemoen 	u8	chip_erase_timeout_max;
193e23741f4SHaavard Skinnemoen 	u8	dev_size;
194e23741f4SHaavard Skinnemoen 	u16	interface_desc;
195e23741f4SHaavard Skinnemoen 	u16	max_buf_write_size;
196e23741f4SHaavard Skinnemoen 	u8	num_erase_regions;
197e23741f4SHaavard Skinnemoen 	u32	erase_region_info[NUM_ERASE_REGIONS];
198e23741f4SHaavard Skinnemoen } __attribute__((packed));
199e23741f4SHaavard Skinnemoen 
200e23741f4SHaavard Skinnemoen struct cfi_pri_hdr {
201e23741f4SHaavard Skinnemoen 	u8	pri[3];
202e23741f4SHaavard Skinnemoen 	u8	major_version;
203e23741f4SHaavard Skinnemoen 	u8	minor_version;
204e23741f4SHaavard Skinnemoen } __attribute__((packed));
205e23741f4SHaavard Skinnemoen 
206cdbaefb5SHaavard Skinnemoen static void flash_write8(u8 value, void *addr)
207cdbaefb5SHaavard Skinnemoen {
208cdbaefb5SHaavard Skinnemoen 	__raw_writeb(value, addr);
209cdbaefb5SHaavard Skinnemoen }
210cdbaefb5SHaavard Skinnemoen 
211cdbaefb5SHaavard Skinnemoen static void flash_write16(u16 value, void *addr)
212cdbaefb5SHaavard Skinnemoen {
213cdbaefb5SHaavard Skinnemoen 	__raw_writew(value, addr);
214cdbaefb5SHaavard Skinnemoen }
215cdbaefb5SHaavard Skinnemoen 
216cdbaefb5SHaavard Skinnemoen static void flash_write32(u32 value, void *addr)
217cdbaefb5SHaavard Skinnemoen {
218cdbaefb5SHaavard Skinnemoen 	__raw_writel(value, addr);
219cdbaefb5SHaavard Skinnemoen }
220cdbaefb5SHaavard Skinnemoen 
221cdbaefb5SHaavard Skinnemoen static void flash_write64(u64 value, void *addr)
222cdbaefb5SHaavard Skinnemoen {
223cdbaefb5SHaavard Skinnemoen 	/* No architectures currently implement __raw_writeq() */
224cdbaefb5SHaavard Skinnemoen 	*(volatile u64 *)addr = value;
225cdbaefb5SHaavard Skinnemoen }
226cdbaefb5SHaavard Skinnemoen 
227cdbaefb5SHaavard Skinnemoen static u8 flash_read8(void *addr)
228cdbaefb5SHaavard Skinnemoen {
229cdbaefb5SHaavard Skinnemoen 	return __raw_readb(addr);
230cdbaefb5SHaavard Skinnemoen }
231cdbaefb5SHaavard Skinnemoen 
232cdbaefb5SHaavard Skinnemoen static u16 flash_read16(void *addr)
233cdbaefb5SHaavard Skinnemoen {
234cdbaefb5SHaavard Skinnemoen 	return __raw_readw(addr);
235cdbaefb5SHaavard Skinnemoen }
236cdbaefb5SHaavard Skinnemoen 
237cdbaefb5SHaavard Skinnemoen static u32 flash_read32(void *addr)
238cdbaefb5SHaavard Skinnemoen {
239cdbaefb5SHaavard Skinnemoen 	return __raw_readl(addr);
240cdbaefb5SHaavard Skinnemoen }
241cdbaefb5SHaavard Skinnemoen 
242*97bf85d7SDaniel Hellstrom static u64 __flash_read64(void *addr)
243cdbaefb5SHaavard Skinnemoen {
244cdbaefb5SHaavard Skinnemoen 	/* No architectures currently implement __raw_readq() */
245cdbaefb5SHaavard Skinnemoen 	return *(volatile u64 *)addr;
246cdbaefb5SHaavard Skinnemoen }
247cdbaefb5SHaavard Skinnemoen 
248*97bf85d7SDaniel Hellstrom u64 flash_read64(void *addr)__attribute__((weak, alias("__flash_read64")));
249*97bf85d7SDaniel Hellstrom 
250be60a902SHaavard Skinnemoen /*-----------------------------------------------------------------------
251be60a902SHaavard Skinnemoen  */
25259829cc1SJean-Christophe PLAGNIOL-VILLARD #if defined(CFG_ENV_IS_IN_FLASH) || defined(CFG_ENV_ADDR_REDUND) || (CFG_MONITOR_BASE >= CFG_FLASH_BASE)
253be60a902SHaavard Skinnemoen static flash_info_t *flash_get_info(ulong base)
254be60a902SHaavard Skinnemoen {
255be60a902SHaavard Skinnemoen 	int i;
256be60a902SHaavard Skinnemoen 	flash_info_t * info = 0;
257be60a902SHaavard Skinnemoen 
258be60a902SHaavard Skinnemoen 	for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
259be60a902SHaavard Skinnemoen 		info = & flash_info[i];
260be60a902SHaavard Skinnemoen 		if (info->size && info->start[0] <= base &&
261be60a902SHaavard Skinnemoen 		    base <= info->start[0] + info->size - 1)
262be60a902SHaavard Skinnemoen 			break;
263be60a902SHaavard Skinnemoen 	}
264be60a902SHaavard Skinnemoen 
265be60a902SHaavard Skinnemoen 	return i == CFG_MAX_FLASH_BANKS ? 0 : info;
266be60a902SHaavard Skinnemoen }
26759829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
26859829cc1SJean-Christophe PLAGNIOL-VILLARD 
26912d30aa7SHaavard Skinnemoen unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect)
27012d30aa7SHaavard Skinnemoen {
27112d30aa7SHaavard Skinnemoen 	if (sect != (info->sector_count - 1))
27212d30aa7SHaavard Skinnemoen 		return info->start[sect + 1] - info->start[sect];
27312d30aa7SHaavard Skinnemoen 	else
27412d30aa7SHaavard Skinnemoen 		return info->start[0] + info->size - info->start[sect];
27512d30aa7SHaavard Skinnemoen }
27612d30aa7SHaavard Skinnemoen 
27759829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
27859829cc1SJean-Christophe PLAGNIOL-VILLARD  * create an address based on the offset and the port width
27959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
28012d30aa7SHaavard Skinnemoen static inline void *
28112d30aa7SHaavard Skinnemoen flash_map (flash_info_t * info, flash_sect_t sect, uint offset)
28259829cc1SJean-Christophe PLAGNIOL-VILLARD {
28312d30aa7SHaavard Skinnemoen 	unsigned int byte_offset = offset * info->portwidth;
28412d30aa7SHaavard Skinnemoen 
28512d30aa7SHaavard Skinnemoen 	return map_physmem(info->start[sect] + byte_offset,
28612d30aa7SHaavard Skinnemoen 			flash_sector_size(info, sect) - byte_offset,
28712d30aa7SHaavard Skinnemoen 			MAP_NOCACHE);
28812d30aa7SHaavard Skinnemoen }
28912d30aa7SHaavard Skinnemoen 
29012d30aa7SHaavard Skinnemoen static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
29112d30aa7SHaavard Skinnemoen 		unsigned int offset, void *addr)
29212d30aa7SHaavard Skinnemoen {
29312d30aa7SHaavard Skinnemoen 	unsigned int byte_offset = offset * info->portwidth;
29412d30aa7SHaavard Skinnemoen 
29512d30aa7SHaavard Skinnemoen 	unmap_physmem(addr, flash_sector_size(info, sect) - byte_offset);
29659829cc1SJean-Christophe PLAGNIOL-VILLARD }
29759829cc1SJean-Christophe PLAGNIOL-VILLARD 
298be60a902SHaavard Skinnemoen /*-----------------------------------------------------------------------
299be60a902SHaavard Skinnemoen  * make a proper sized command based on the port and chip widths
300be60a902SHaavard Skinnemoen  */
301be60a902SHaavard Skinnemoen static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf)
302be60a902SHaavard Skinnemoen {
303be60a902SHaavard Skinnemoen 	int i;
304be60a902SHaavard Skinnemoen 	uchar *cp = (uchar *) cmdbuf;
305be60a902SHaavard Skinnemoen 
306be60a902SHaavard Skinnemoen #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
307be60a902SHaavard Skinnemoen 	for (i = info->portwidth; i > 0; i--)
308be60a902SHaavard Skinnemoen #else
309be60a902SHaavard Skinnemoen 	for (i = 1; i <= info->portwidth; i++)
310be60a902SHaavard Skinnemoen #endif
311be60a902SHaavard Skinnemoen 		*cp++ = (i & (info->chipwidth - 1)) ? '\0' : cmd;
312be60a902SHaavard Skinnemoen }
313be60a902SHaavard Skinnemoen 
31459829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG
31559829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
31659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Debug support
31759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
3183055793bSHaavard Skinnemoen static void print_longlong (char *str, unsigned long long data)
31959829cc1SJean-Christophe PLAGNIOL-VILLARD {
32059829cc1SJean-Christophe PLAGNIOL-VILLARD 	int i;
32159829cc1SJean-Christophe PLAGNIOL-VILLARD 	char *cp;
32259829cc1SJean-Christophe PLAGNIOL-VILLARD 
32359829cc1SJean-Christophe PLAGNIOL-VILLARD 	cp = (unsigned char *) &data;
32459829cc1SJean-Christophe PLAGNIOL-VILLARD 	for (i = 0; i < 8; i++)
32559829cc1SJean-Christophe PLAGNIOL-VILLARD 		sprintf (&str[i * 2], "%2.2x", *cp++);
32659829cc1SJean-Christophe PLAGNIOL-VILLARD }
327be60a902SHaavard Skinnemoen 
328e23741f4SHaavard Skinnemoen static void flash_printqry (struct cfi_qry *qry)
32959829cc1SJean-Christophe PLAGNIOL-VILLARD {
330e23741f4SHaavard Skinnemoen 	u8 *p = (u8 *)qry;
33159829cc1SJean-Christophe PLAGNIOL-VILLARD 	int x, y;
33259829cc1SJean-Christophe PLAGNIOL-VILLARD 
333e23741f4SHaavard Skinnemoen 	for (x = 0; x < sizeof(struct cfi_qry); x += 16) {
334e23741f4SHaavard Skinnemoen 		debug("%02x : ", x);
335e23741f4SHaavard Skinnemoen 		for (y = 0; y < 16; y++)
336e23741f4SHaavard Skinnemoen 			debug("%2.2x ", p[x + y]);
33759829cc1SJean-Christophe PLAGNIOL-VILLARD 		debug(" ");
33859829cc1SJean-Christophe PLAGNIOL-VILLARD 		for (y = 0; y < 16; y++) {
339e23741f4SHaavard Skinnemoen 			unsigned char c = p[x + y];
340e23741f4SHaavard Skinnemoen 			if (c >= 0x20 && c <= 0x7e)
341cdbaefb5SHaavard Skinnemoen 				debug("%c", c);
342e23741f4SHaavard Skinnemoen 			else
34359829cc1SJean-Christophe PLAGNIOL-VILLARD 				debug(".");
34459829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
34559829cc1SJean-Christophe PLAGNIOL-VILLARD 		debug("\n");
34659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
34759829cc1SJean-Christophe PLAGNIOL-VILLARD }
34859829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
34959829cc1SJean-Christophe PLAGNIOL-VILLARD 
35059829cc1SJean-Christophe PLAGNIOL-VILLARD 
35159829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
35259829cc1SJean-Christophe PLAGNIOL-VILLARD  * read a character at a port width address
35359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
3543055793bSHaavard Skinnemoen static inline uchar flash_read_uchar (flash_info_t * info, uint offset)
35559829cc1SJean-Christophe PLAGNIOL-VILLARD {
35659829cc1SJean-Christophe PLAGNIOL-VILLARD 	uchar *cp;
35712d30aa7SHaavard Skinnemoen 	uchar retval;
35859829cc1SJean-Christophe PLAGNIOL-VILLARD 
35912d30aa7SHaavard Skinnemoen 	cp = flash_map (info, 0, offset);
36059829cc1SJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
36112d30aa7SHaavard Skinnemoen 	retval = flash_read8(cp);
36259829cc1SJean-Christophe PLAGNIOL-VILLARD #else
36312d30aa7SHaavard Skinnemoen 	retval = flash_read8(cp + info->portwidth - 1);
36459829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
36512d30aa7SHaavard Skinnemoen 	flash_unmap (info, 0, offset, cp);
36612d30aa7SHaavard Skinnemoen 	return retval;
36759829cc1SJean-Christophe PLAGNIOL-VILLARD }
36859829cc1SJean-Christophe PLAGNIOL-VILLARD 
36959829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
37090447ecbSTor Krill  * read a word at a port width address, assume 16bit bus
37190447ecbSTor Krill  */
37290447ecbSTor Krill static inline ushort flash_read_word (flash_info_t * info, uint offset)
37390447ecbSTor Krill {
37490447ecbSTor Krill 	ushort *addr, retval;
37590447ecbSTor Krill 
37690447ecbSTor Krill 	addr = flash_map (info, 0, offset);
37790447ecbSTor Krill 	retval = flash_read16 (addr);
37890447ecbSTor Krill 	flash_unmap (info, 0, offset, addr);
37990447ecbSTor Krill 	return retval;
38090447ecbSTor Krill }
38190447ecbSTor Krill 
38290447ecbSTor Krill 
38390447ecbSTor Krill /*-----------------------------------------------------------------------
38459829cc1SJean-Christophe PLAGNIOL-VILLARD  * read a long word by picking the least significant byte of each maximum
38559829cc1SJean-Christophe PLAGNIOL-VILLARD  * port size word. Swap for ppc format.
38659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
3873055793bSHaavard Skinnemoen static ulong flash_read_long (flash_info_t * info, flash_sect_t sect,
3883055793bSHaavard Skinnemoen 			      uint offset)
38959829cc1SJean-Christophe PLAGNIOL-VILLARD {
39059829cc1SJean-Christophe PLAGNIOL-VILLARD 	uchar *addr;
39159829cc1SJean-Christophe PLAGNIOL-VILLARD 	ulong retval;
39259829cc1SJean-Christophe PLAGNIOL-VILLARD 
39359829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG
39459829cc1SJean-Christophe PLAGNIOL-VILLARD 	int x;
39559829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
39612d30aa7SHaavard Skinnemoen 	addr = flash_map (info, sect, offset);
39759829cc1SJean-Christophe PLAGNIOL-VILLARD 
39859829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG
39959829cc1SJean-Christophe PLAGNIOL-VILLARD 	debug ("long addr is at %p info->portwidth = %d\n", addr,
40059829cc1SJean-Christophe PLAGNIOL-VILLARD 	       info->portwidth);
40159829cc1SJean-Christophe PLAGNIOL-VILLARD 	for (x = 0; x < 4 * info->portwidth; x++) {
40212d30aa7SHaavard Skinnemoen 		debug ("addr[%x] = 0x%x\n", x, flash_read8(addr + x));
40359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
40459829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
40559829cc1SJean-Christophe PLAGNIOL-VILLARD #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
40612d30aa7SHaavard Skinnemoen 	retval = ((flash_read8(addr) << 16) |
40712d30aa7SHaavard Skinnemoen 		  (flash_read8(addr + info->portwidth) << 24) |
40812d30aa7SHaavard Skinnemoen 		  (flash_read8(addr + 2 * info->portwidth)) |
40912d30aa7SHaavard Skinnemoen 		  (flash_read8(addr + 3 * info->portwidth) << 8));
41059829cc1SJean-Christophe PLAGNIOL-VILLARD #else
41112d30aa7SHaavard Skinnemoen 	retval = ((flash_read8(addr + 2 * info->portwidth - 1) << 24) |
41212d30aa7SHaavard Skinnemoen 		  (flash_read8(addr + info->portwidth - 1) << 16) |
41312d30aa7SHaavard Skinnemoen 		  (flash_read8(addr + 4 * info->portwidth - 1) << 8) |
41412d30aa7SHaavard Skinnemoen 		  (flash_read8(addr + 3 * info->portwidth - 1)));
41559829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
41612d30aa7SHaavard Skinnemoen 	flash_unmap(info, sect, offset, addr);
41712d30aa7SHaavard Skinnemoen 
41859829cc1SJean-Christophe PLAGNIOL-VILLARD 	return retval;
41959829cc1SJean-Christophe PLAGNIOL-VILLARD }
42059829cc1SJean-Christophe PLAGNIOL-VILLARD 
421be60a902SHaavard Skinnemoen /*
422be60a902SHaavard Skinnemoen  * Write a proper sized command to the correct address
42381b20cccSMichael Schwingen  */
424be60a902SHaavard Skinnemoen static void flash_write_cmd (flash_info_t * info, flash_sect_t sect,
425be60a902SHaavard Skinnemoen 			     uint offset, uchar cmd)
42681b20cccSMichael Schwingen {
4277e5b9b47SHaavard Skinnemoen 
428cdbaefb5SHaavard Skinnemoen 	void *addr;
429be60a902SHaavard Skinnemoen 	cfiword_t cword;
43081b20cccSMichael Schwingen 
43112d30aa7SHaavard Skinnemoen 	addr = flash_map (info, sect, offset);
432be60a902SHaavard Skinnemoen 	flash_make_cmd (info, cmd, &cword);
433be60a902SHaavard Skinnemoen 	switch (info->portwidth) {
434be60a902SHaavard Skinnemoen 	case FLASH_CFI_8BIT:
435cdbaefb5SHaavard Skinnemoen 		debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr, cmd,
436be60a902SHaavard Skinnemoen 		       cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
437cdbaefb5SHaavard Skinnemoen 		flash_write8(cword.c, addr);
438be60a902SHaavard Skinnemoen 		break;
439be60a902SHaavard Skinnemoen 	case FLASH_CFI_16BIT:
440cdbaefb5SHaavard Skinnemoen 		debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr,
441be60a902SHaavard Skinnemoen 		       cmd, cword.w,
442be60a902SHaavard Skinnemoen 		       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
443cdbaefb5SHaavard Skinnemoen 		flash_write16(cword.w, addr);
444be60a902SHaavard Skinnemoen 		break;
445be60a902SHaavard Skinnemoen 	case FLASH_CFI_32BIT:
446cdbaefb5SHaavard Skinnemoen 		debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit\n", addr,
447be60a902SHaavard Skinnemoen 		       cmd, cword.l,
448be60a902SHaavard Skinnemoen 		       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
449cdbaefb5SHaavard Skinnemoen 		flash_write32(cword.l, addr);
450be60a902SHaavard Skinnemoen 		break;
451be60a902SHaavard Skinnemoen 	case FLASH_CFI_64BIT:
452be60a902SHaavard Skinnemoen #ifdef DEBUG
453be60a902SHaavard Skinnemoen 		{
454be60a902SHaavard Skinnemoen 			char str[20];
455be60a902SHaavard Skinnemoen 
456be60a902SHaavard Skinnemoen 			print_longlong (str, cword.ll);
457be60a902SHaavard Skinnemoen 
458be60a902SHaavard Skinnemoen 			debug ("fwrite addr %p cmd %x %s 64 bit x %d bit\n",
459cdbaefb5SHaavard Skinnemoen 			       addr, cmd, str,
460be60a902SHaavard Skinnemoen 			       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
46181b20cccSMichael Schwingen 		}
462be60a902SHaavard Skinnemoen #endif
463cdbaefb5SHaavard Skinnemoen 		flash_write64(cword.ll, addr);
46481b20cccSMichael Schwingen 		break;
46581b20cccSMichael Schwingen 	}
466be60a902SHaavard Skinnemoen 
467be60a902SHaavard Skinnemoen 	/* Ensure all the instructions are fully finished */
468be60a902SHaavard Skinnemoen 	sync();
46912d30aa7SHaavard Skinnemoen 
47012d30aa7SHaavard Skinnemoen 	flash_unmap(info, sect, offset, addr);
47181b20cccSMichael Schwingen }
4727e5b9b47SHaavard Skinnemoen 
473be60a902SHaavard Skinnemoen static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect)
474be60a902SHaavard Skinnemoen {
475be60a902SHaavard Skinnemoen 	flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_UNLOCK_START);
476be60a902SHaavard Skinnemoen 	flash_write_cmd (info, sect, info->addr_unlock2, AMD_CMD_UNLOCK_ACK);
477be60a902SHaavard Skinnemoen }
478be60a902SHaavard Skinnemoen 
479be60a902SHaavard Skinnemoen /*-----------------------------------------------------------------------
480be60a902SHaavard Skinnemoen  */
481be60a902SHaavard Skinnemoen static int flash_isequal (flash_info_t * info, flash_sect_t sect,
482be60a902SHaavard Skinnemoen 			  uint offset, uchar cmd)
483be60a902SHaavard Skinnemoen {
484cdbaefb5SHaavard Skinnemoen 	void *addr;
485be60a902SHaavard Skinnemoen 	cfiword_t cword;
486be60a902SHaavard Skinnemoen 	int retval;
487be60a902SHaavard Skinnemoen 
48812d30aa7SHaavard Skinnemoen 	addr = flash_map (info, sect, offset);
489be60a902SHaavard Skinnemoen 	flash_make_cmd (info, cmd, &cword);
490be60a902SHaavard Skinnemoen 
491cdbaefb5SHaavard Skinnemoen 	debug ("is= cmd %x(%c) addr %p ", cmd, cmd, addr);
492be60a902SHaavard Skinnemoen 	switch (info->portwidth) {
493be60a902SHaavard Skinnemoen 	case FLASH_CFI_8BIT:
494cdbaefb5SHaavard Skinnemoen 		debug ("is= %x %x\n", flash_read8(addr), cword.c);
495cdbaefb5SHaavard Skinnemoen 		retval = (flash_read8(addr) == cword.c);
496be60a902SHaavard Skinnemoen 		break;
497be60a902SHaavard Skinnemoen 	case FLASH_CFI_16BIT:
498cdbaefb5SHaavard Skinnemoen 		debug ("is= %4.4x %4.4x\n", flash_read16(addr), cword.w);
499cdbaefb5SHaavard Skinnemoen 		retval = (flash_read16(addr) == cword.w);
500be60a902SHaavard Skinnemoen 		break;
501be60a902SHaavard Skinnemoen 	case FLASH_CFI_32BIT:
502cdbaefb5SHaavard Skinnemoen 		debug ("is= %8.8lx %8.8lx\n", flash_read32(addr), cword.l);
503cdbaefb5SHaavard Skinnemoen 		retval = (flash_read32(addr) == cword.l);
504be60a902SHaavard Skinnemoen 		break;
505be60a902SHaavard Skinnemoen 	case FLASH_CFI_64BIT:
506be60a902SHaavard Skinnemoen #ifdef DEBUG
507be60a902SHaavard Skinnemoen 		{
508be60a902SHaavard Skinnemoen 			char str1[20];
509be60a902SHaavard Skinnemoen 			char str2[20];
510be60a902SHaavard Skinnemoen 
511cdbaefb5SHaavard Skinnemoen 			print_longlong (str1, flash_read64(addr));
512be60a902SHaavard Skinnemoen 			print_longlong (str2, cword.ll);
513be60a902SHaavard Skinnemoen 			debug ("is= %s %s\n", str1, str2);
514be60a902SHaavard Skinnemoen 		}
515be60a902SHaavard Skinnemoen #endif
516cdbaefb5SHaavard Skinnemoen 		retval = (flash_read64(addr) == cword.ll);
517be60a902SHaavard Skinnemoen 		break;
518be60a902SHaavard Skinnemoen 	default:
519be60a902SHaavard Skinnemoen 		retval = 0;
520be60a902SHaavard Skinnemoen 		break;
521be60a902SHaavard Skinnemoen 	}
52212d30aa7SHaavard Skinnemoen 	flash_unmap(info, sect, offset, addr);
52312d30aa7SHaavard Skinnemoen 
524be60a902SHaavard Skinnemoen 	return retval;
525be60a902SHaavard Skinnemoen }
526be60a902SHaavard Skinnemoen 
527be60a902SHaavard Skinnemoen /*-----------------------------------------------------------------------
528be60a902SHaavard Skinnemoen  */
529be60a902SHaavard Skinnemoen static int flash_isset (flash_info_t * info, flash_sect_t sect,
530be60a902SHaavard Skinnemoen 			uint offset, uchar cmd)
531be60a902SHaavard Skinnemoen {
532cdbaefb5SHaavard Skinnemoen 	void *addr;
533be60a902SHaavard Skinnemoen 	cfiword_t cword;
534be60a902SHaavard Skinnemoen 	int retval;
535be60a902SHaavard Skinnemoen 
53612d30aa7SHaavard Skinnemoen 	addr = flash_map (info, sect, offset);
537be60a902SHaavard Skinnemoen 	flash_make_cmd (info, cmd, &cword);
538be60a902SHaavard Skinnemoen 	switch (info->portwidth) {
539be60a902SHaavard Skinnemoen 	case FLASH_CFI_8BIT:
540cdbaefb5SHaavard Skinnemoen 		retval = ((flash_read8(addr) & cword.c) == cword.c);
541be60a902SHaavard Skinnemoen 		break;
542be60a902SHaavard Skinnemoen 	case FLASH_CFI_16BIT:
543cdbaefb5SHaavard Skinnemoen 		retval = ((flash_read16(addr) & cword.w) == cword.w);
544be60a902SHaavard Skinnemoen 		break;
545be60a902SHaavard Skinnemoen 	case FLASH_CFI_32BIT:
54647cc23cbSStefan Roese 		retval = ((flash_read32(addr) & cword.l) == cword.l);
547be60a902SHaavard Skinnemoen 		break;
548be60a902SHaavard Skinnemoen 	case FLASH_CFI_64BIT:
549cdbaefb5SHaavard Skinnemoen 		retval = ((flash_read64(addr) & cword.ll) == cword.ll);
550be60a902SHaavard Skinnemoen 		break;
551be60a902SHaavard Skinnemoen 	default:
552be60a902SHaavard Skinnemoen 		retval = 0;
553be60a902SHaavard Skinnemoen 		break;
554be60a902SHaavard Skinnemoen 	}
55512d30aa7SHaavard Skinnemoen 	flash_unmap(info, sect, offset, addr);
55612d30aa7SHaavard Skinnemoen 
557be60a902SHaavard Skinnemoen 	return retval;
558be60a902SHaavard Skinnemoen }
559be60a902SHaavard Skinnemoen 
560be60a902SHaavard Skinnemoen /*-----------------------------------------------------------------------
561be60a902SHaavard Skinnemoen  */
562be60a902SHaavard Skinnemoen static int flash_toggle (flash_info_t * info, flash_sect_t sect,
563be60a902SHaavard Skinnemoen 			 uint offset, uchar cmd)
564be60a902SHaavard Skinnemoen {
565cdbaefb5SHaavard Skinnemoen 	void *addr;
566be60a902SHaavard Skinnemoen 	cfiword_t cword;
567be60a902SHaavard Skinnemoen 	int retval;
568be60a902SHaavard Skinnemoen 
56912d30aa7SHaavard Skinnemoen 	addr = flash_map (info, sect, offset);
570be60a902SHaavard Skinnemoen 	flash_make_cmd (info, cmd, &cword);
571be60a902SHaavard Skinnemoen 	switch (info->portwidth) {
572be60a902SHaavard Skinnemoen 	case FLASH_CFI_8BIT:
573cdbaefb5SHaavard Skinnemoen 		retval = ((flash_read8(addr) & cword.c) !=
574cdbaefb5SHaavard Skinnemoen 			  (flash_read8(addr) & cword.c));
575be60a902SHaavard Skinnemoen 		break;
576be60a902SHaavard Skinnemoen 	case FLASH_CFI_16BIT:
577cdbaefb5SHaavard Skinnemoen 		retval = ((flash_read16(addr) & cword.w) !=
578cdbaefb5SHaavard Skinnemoen 			  (flash_read16(addr) & cword.w));
579be60a902SHaavard Skinnemoen 		break;
580be60a902SHaavard Skinnemoen 	case FLASH_CFI_32BIT:
581cdbaefb5SHaavard Skinnemoen 		retval = ((flash_read32(addr) & cword.l) !=
582cdbaefb5SHaavard Skinnemoen 			  (flash_read32(addr) & cword.l));
583be60a902SHaavard Skinnemoen 		break;
584be60a902SHaavard Skinnemoen 	case FLASH_CFI_64BIT:
585cdbaefb5SHaavard Skinnemoen 		retval = ((flash_read64(addr) & cword.ll) !=
586cdbaefb5SHaavard Skinnemoen 			  (flash_read64(addr) & cword.ll));
587be60a902SHaavard Skinnemoen 		break;
588be60a902SHaavard Skinnemoen 	default:
589be60a902SHaavard Skinnemoen 		retval = 0;
590be60a902SHaavard Skinnemoen 		break;
591be60a902SHaavard Skinnemoen 	}
59212d30aa7SHaavard Skinnemoen 	flash_unmap(info, sect, offset, addr);
59312d30aa7SHaavard Skinnemoen 
594be60a902SHaavard Skinnemoen 	return retval;
595be60a902SHaavard Skinnemoen }
596be60a902SHaavard Skinnemoen 
597be60a902SHaavard Skinnemoen /*
598be60a902SHaavard Skinnemoen  * flash_is_busy - check to see if the flash is busy
599be60a902SHaavard Skinnemoen  *
600be60a902SHaavard Skinnemoen  * This routine checks the status of the chip and returns true if the
601be60a902SHaavard Skinnemoen  * chip is busy.
602be60a902SHaavard Skinnemoen  */
603be60a902SHaavard Skinnemoen static int flash_is_busy (flash_info_t * info, flash_sect_t sect)
604be60a902SHaavard Skinnemoen {
605be60a902SHaavard Skinnemoen 	int retval;
606be60a902SHaavard Skinnemoen 
60781b20cccSMichael Schwingen 	switch (info->vendor) {
60881b20cccSMichael Schwingen 	case CFI_CMDSET_INTEL_STANDARD:
60981b20cccSMichael Schwingen 	case CFI_CMDSET_INTEL_EXTENDED:
610be60a902SHaavard Skinnemoen 		retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE);
61181b20cccSMichael Schwingen 		break;
61281b20cccSMichael Schwingen 	case CFI_CMDSET_AMD_STANDARD:
61381b20cccSMichael Schwingen 	case CFI_CMDSET_AMD_EXTENDED:
614be60a902SHaavard Skinnemoen #ifdef CONFIG_FLASH_CFI_LEGACY
61581b20cccSMichael Schwingen 	case CFI_CMDSET_AMD_LEGACY:
616be60a902SHaavard Skinnemoen #endif
617be60a902SHaavard Skinnemoen 		retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE);
618be60a902SHaavard Skinnemoen 		break;
619be60a902SHaavard Skinnemoen 	default:
620be60a902SHaavard Skinnemoen 		retval = 0;
621be60a902SHaavard Skinnemoen 	}
622be60a902SHaavard Skinnemoen 	debug ("flash_is_busy: %d\n", retval);
623be60a902SHaavard Skinnemoen 	return retval;
624be60a902SHaavard Skinnemoen }
625be60a902SHaavard Skinnemoen 
626be60a902SHaavard Skinnemoen /*-----------------------------------------------------------------------
627be60a902SHaavard Skinnemoen  *  wait for XSR.7 to be set. Time out with an error if it does not.
628be60a902SHaavard Skinnemoen  *  This routine does not set the flash to read-array mode.
629be60a902SHaavard Skinnemoen  */
630be60a902SHaavard Skinnemoen static int flash_status_check (flash_info_t * info, flash_sect_t sector,
631be60a902SHaavard Skinnemoen 			       ulong tout, char *prompt)
632be60a902SHaavard Skinnemoen {
633be60a902SHaavard Skinnemoen 	ulong start;
634be60a902SHaavard Skinnemoen 
635be60a902SHaavard Skinnemoen #if CFG_HZ != 1000
636be60a902SHaavard Skinnemoen 	tout *= CFG_HZ/1000;
637be60a902SHaavard Skinnemoen #endif
638be60a902SHaavard Skinnemoen 
639be60a902SHaavard Skinnemoen 	/* Wait for command completion */
640be60a902SHaavard Skinnemoen 	start = get_timer (0);
641be60a902SHaavard Skinnemoen 	while (flash_is_busy (info, sector)) {
642be60a902SHaavard Skinnemoen 		if (get_timer (start) > tout) {
643be60a902SHaavard Skinnemoen 			printf ("Flash %s timeout at address %lx data %lx\n",
644be60a902SHaavard Skinnemoen 				prompt, info->start[sector],
645be60a902SHaavard Skinnemoen 				flash_read_long (info, sector, 0));
646be60a902SHaavard Skinnemoen 			flash_write_cmd (info, sector, 0, info->cmd_reset);
647be60a902SHaavard Skinnemoen 			return ERR_TIMOUT;
648be60a902SHaavard Skinnemoen 		}
649be60a902SHaavard Skinnemoen 		udelay (1);		/* also triggers watchdog */
650be60a902SHaavard Skinnemoen 	}
651be60a902SHaavard Skinnemoen 	return ERR_OK;
652be60a902SHaavard Skinnemoen }
653be60a902SHaavard Skinnemoen 
654be60a902SHaavard Skinnemoen /*-----------------------------------------------------------------------
655be60a902SHaavard Skinnemoen  * Wait for XSR.7 to be set, if it times out print an error, otherwise
656be60a902SHaavard Skinnemoen  * do a full status check.
657be60a902SHaavard Skinnemoen  *
658be60a902SHaavard Skinnemoen  * This routine sets the flash to read-array mode.
659be60a902SHaavard Skinnemoen  */
660be60a902SHaavard Skinnemoen static int flash_full_status_check (flash_info_t * info, flash_sect_t sector,
661be60a902SHaavard Skinnemoen 				    ulong tout, char *prompt)
662be60a902SHaavard Skinnemoen {
663be60a902SHaavard Skinnemoen 	int retcode;
664be60a902SHaavard Skinnemoen 
665be60a902SHaavard Skinnemoen 	retcode = flash_status_check (info, sector, tout, prompt);
666be60a902SHaavard Skinnemoen 	switch (info->vendor) {
667be60a902SHaavard Skinnemoen 	case CFI_CMDSET_INTEL_EXTENDED:
668be60a902SHaavard Skinnemoen 	case CFI_CMDSET_INTEL_STANDARD:
669be60a902SHaavard Skinnemoen 		if ((retcode == ERR_OK)
670be60a902SHaavard Skinnemoen 		    && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) {
671be60a902SHaavard Skinnemoen 			retcode = ERR_INVAL;
672be60a902SHaavard Skinnemoen 			printf ("Flash %s error at address %lx\n", prompt,
673be60a902SHaavard Skinnemoen 				info->start[sector]);
674be60a902SHaavard Skinnemoen 			if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS |
675be60a902SHaavard Skinnemoen 					 FLASH_STATUS_PSLBS)) {
676be60a902SHaavard Skinnemoen 				puts ("Command Sequence Error.\n");
677be60a902SHaavard Skinnemoen 			} else if (flash_isset (info, sector, 0,
678be60a902SHaavard Skinnemoen 						FLASH_STATUS_ECLBS)) {
679be60a902SHaavard Skinnemoen 				puts ("Block Erase Error.\n");
680be60a902SHaavard Skinnemoen 				retcode = ERR_NOT_ERASED;
681be60a902SHaavard Skinnemoen 			} else if (flash_isset (info, sector, 0,
682be60a902SHaavard Skinnemoen 						FLASH_STATUS_PSLBS)) {
683be60a902SHaavard Skinnemoen 				puts ("Locking Error\n");
684be60a902SHaavard Skinnemoen 			}
685be60a902SHaavard Skinnemoen 			if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) {
686be60a902SHaavard Skinnemoen 				puts ("Block locked.\n");
687be60a902SHaavard Skinnemoen 				retcode = ERR_PROTECTED;
688be60a902SHaavard Skinnemoen 			}
689be60a902SHaavard Skinnemoen 			if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS))
690be60a902SHaavard Skinnemoen 				puts ("Vpp Low Error.\n");
691be60a902SHaavard Skinnemoen 		}
692be60a902SHaavard Skinnemoen 		flash_write_cmd (info, sector, 0, info->cmd_reset);
693be60a902SHaavard Skinnemoen 		break;
694be60a902SHaavard Skinnemoen 	default:
69581b20cccSMichael Schwingen 		break;
69681b20cccSMichael Schwingen 	}
697be60a902SHaavard Skinnemoen 	return retcode;
69881b20cccSMichael Schwingen }
699be60a902SHaavard Skinnemoen 
700be60a902SHaavard Skinnemoen /*-----------------------------------------------------------------------
701be60a902SHaavard Skinnemoen  */
702be60a902SHaavard Skinnemoen static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
703be60a902SHaavard Skinnemoen {
704be60a902SHaavard Skinnemoen #if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
705be60a902SHaavard Skinnemoen 	unsigned short	w;
706be60a902SHaavard Skinnemoen 	unsigned int	l;
707be60a902SHaavard Skinnemoen 	unsigned long long ll;
708be60a902SHaavard Skinnemoen #endif
709be60a902SHaavard Skinnemoen 
710be60a902SHaavard Skinnemoen 	switch (info->portwidth) {
711be60a902SHaavard Skinnemoen 	case FLASH_CFI_8BIT:
712be60a902SHaavard Skinnemoen 		cword->c = c;
713be60a902SHaavard Skinnemoen 		break;
714be60a902SHaavard Skinnemoen 	case FLASH_CFI_16BIT:
715be60a902SHaavard Skinnemoen #if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
716be60a902SHaavard Skinnemoen 		w = c;
717be60a902SHaavard Skinnemoen 		w <<= 8;
718be60a902SHaavard Skinnemoen 		cword->w = (cword->w >> 8) | w;
71981b20cccSMichael Schwingen #else
720be60a902SHaavard Skinnemoen 		cword->w = (cword->w << 8) | c;
721be60a902SHaavard Skinnemoen #endif
722be60a902SHaavard Skinnemoen 		break;
723be60a902SHaavard Skinnemoen 	case FLASH_CFI_32BIT:
724be60a902SHaavard Skinnemoen #if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
725be60a902SHaavard Skinnemoen 		l = c;
726be60a902SHaavard Skinnemoen 		l <<= 24;
727be60a902SHaavard Skinnemoen 		cword->l = (cword->l >> 8) | l;
728be60a902SHaavard Skinnemoen #else
729be60a902SHaavard Skinnemoen 		cword->l = (cword->l << 8) | c;
730be60a902SHaavard Skinnemoen #endif
731be60a902SHaavard Skinnemoen 		break;
732be60a902SHaavard Skinnemoen 	case FLASH_CFI_64BIT:
733be60a902SHaavard Skinnemoen #if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
734be60a902SHaavard Skinnemoen 		ll = c;
735be60a902SHaavard Skinnemoen 		ll <<= 56;
736be60a902SHaavard Skinnemoen 		cword->ll = (cword->ll >> 8) | ll;
737be60a902SHaavard Skinnemoen #else
738be60a902SHaavard Skinnemoen 		cword->ll = (cword->ll << 8) | c;
739be60a902SHaavard Skinnemoen #endif
740be60a902SHaavard Skinnemoen 		break;
741be60a902SHaavard Skinnemoen 	}
742be60a902SHaavard Skinnemoen }
743be60a902SHaavard Skinnemoen 
744be60a902SHaavard Skinnemoen /* loop through the sectors from the highest address when the passed
745be60a902SHaavard Skinnemoen  * address is greater or equal to the sector address we have a match
746be60a902SHaavard Skinnemoen  */
747be60a902SHaavard Skinnemoen static flash_sect_t find_sector (flash_info_t * info, ulong addr)
74881b20cccSMichael Schwingen {
749be60a902SHaavard Skinnemoen 	flash_sect_t sector;
750be60a902SHaavard Skinnemoen 
751be60a902SHaavard Skinnemoen 	for (sector = info->sector_count - 1; sector >= 0; sector--) {
752be60a902SHaavard Skinnemoen 		if (addr >= info->start[sector])
753be60a902SHaavard Skinnemoen 			break;
75481b20cccSMichael Schwingen 	}
755be60a902SHaavard Skinnemoen 	return sector;
75659829cc1SJean-Christophe PLAGNIOL-VILLARD }
75759829cc1SJean-Christophe PLAGNIOL-VILLARD 
75859829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
75959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
760be60a902SHaavard Skinnemoen static int flash_write_cfiword (flash_info_t * info, ulong dest,
761be60a902SHaavard Skinnemoen 				cfiword_t cword)
76259829cc1SJean-Christophe PLAGNIOL-VILLARD {
763cdbaefb5SHaavard Skinnemoen 	void *dstaddr;
764be60a902SHaavard Skinnemoen 	int flag;
76559829cc1SJean-Christophe PLAGNIOL-VILLARD 
76612d30aa7SHaavard Skinnemoen 	dstaddr = map_physmem(dest, info->portwidth, MAP_NOCACHE);
767be60a902SHaavard Skinnemoen 
768be60a902SHaavard Skinnemoen 	/* Check if Flash is (sufficiently) erased */
769be60a902SHaavard Skinnemoen 	switch (info->portwidth) {
770be60a902SHaavard Skinnemoen 	case FLASH_CFI_8BIT:
771cdbaefb5SHaavard Skinnemoen 		flag = ((flash_read8(dstaddr) & cword.c) == cword.c);
772be60a902SHaavard Skinnemoen 		break;
773be60a902SHaavard Skinnemoen 	case FLASH_CFI_16BIT:
774cdbaefb5SHaavard Skinnemoen 		flag = ((flash_read16(dstaddr) & cword.w) == cword.w);
775be60a902SHaavard Skinnemoen 		break;
776be60a902SHaavard Skinnemoen 	case FLASH_CFI_32BIT:
777cdbaefb5SHaavard Skinnemoen 		flag = ((flash_read32(dstaddr) & cword.l) == cword.l);
778be60a902SHaavard Skinnemoen 		break;
779be60a902SHaavard Skinnemoen 	case FLASH_CFI_64BIT:
780cdbaefb5SHaavard Skinnemoen 		flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll);
781be60a902SHaavard Skinnemoen 		break;
782be60a902SHaavard Skinnemoen 	default:
78312d30aa7SHaavard Skinnemoen 		flag = 0;
78412d30aa7SHaavard Skinnemoen 		break;
78512d30aa7SHaavard Skinnemoen 	}
78612d30aa7SHaavard Skinnemoen 	if (!flag) {
78712d30aa7SHaavard Skinnemoen 		unmap_physmem(dstaddr, info->portwidth);
7880dc80e27SStefan Roese 		return ERR_NOT_ERASED;
789be60a902SHaavard Skinnemoen 	}
790be60a902SHaavard Skinnemoen 
791be60a902SHaavard Skinnemoen 	/* Disable interrupts which might cause a timeout here */
792be60a902SHaavard Skinnemoen 	flag = disable_interrupts ();
793be60a902SHaavard Skinnemoen 
794be60a902SHaavard Skinnemoen 	switch (info->vendor) {
795be60a902SHaavard Skinnemoen 	case CFI_CMDSET_INTEL_EXTENDED:
796be60a902SHaavard Skinnemoen 	case CFI_CMDSET_INTEL_STANDARD:
797be60a902SHaavard Skinnemoen 		flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS);
798be60a902SHaavard Skinnemoen 		flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE);
799be60a902SHaavard Skinnemoen 		break;
800be60a902SHaavard Skinnemoen 	case CFI_CMDSET_AMD_EXTENDED:
801be60a902SHaavard Skinnemoen 	case CFI_CMDSET_AMD_STANDARD:
802be60a902SHaavard Skinnemoen #ifdef CONFIG_FLASH_CFI_LEGACY
803be60a902SHaavard Skinnemoen 	case CFI_CMDSET_AMD_LEGACY:
804be60a902SHaavard Skinnemoen #endif
805be60a902SHaavard Skinnemoen 		flash_unlock_seq (info, 0);
806be60a902SHaavard Skinnemoen 		flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_WRITE);
80759829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
80859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
80959829cc1SJean-Christophe PLAGNIOL-VILLARD 
810be60a902SHaavard Skinnemoen 	switch (info->portwidth) {
811be60a902SHaavard Skinnemoen 	case FLASH_CFI_8BIT:
812cdbaefb5SHaavard Skinnemoen 		flash_write8(cword.c, dstaddr);
813be60a902SHaavard Skinnemoen 		break;
814be60a902SHaavard Skinnemoen 	case FLASH_CFI_16BIT:
815cdbaefb5SHaavard Skinnemoen 		flash_write16(cword.w, dstaddr);
816be60a902SHaavard Skinnemoen 		break;
817be60a902SHaavard Skinnemoen 	case FLASH_CFI_32BIT:
818cdbaefb5SHaavard Skinnemoen 		flash_write32(cword.l, dstaddr);
819be60a902SHaavard Skinnemoen 		break;
820be60a902SHaavard Skinnemoen 	case FLASH_CFI_64BIT:
821cdbaefb5SHaavard Skinnemoen 		flash_write64(cword.ll, dstaddr);
822be60a902SHaavard Skinnemoen 		break;
82359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
824be60a902SHaavard Skinnemoen 
825be60a902SHaavard Skinnemoen 	/* re-enable interrupts if necessary */
826be60a902SHaavard Skinnemoen 	if (flag)
827be60a902SHaavard Skinnemoen 		enable_interrupts ();
828be60a902SHaavard Skinnemoen 
82912d30aa7SHaavard Skinnemoen 	unmap_physmem(dstaddr, info->portwidth);
83012d30aa7SHaavard Skinnemoen 
831be60a902SHaavard Skinnemoen 	return flash_full_status_check (info, find_sector (info, dest),
832be60a902SHaavard Skinnemoen 					info->write_tout, "write");
833be60a902SHaavard Skinnemoen }
834be60a902SHaavard Skinnemoen 
835be60a902SHaavard Skinnemoen #ifdef CFG_FLASH_USE_BUFFER_WRITE
836be60a902SHaavard Skinnemoen 
837be60a902SHaavard Skinnemoen static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp,
838be60a902SHaavard Skinnemoen 				  int len)
839be60a902SHaavard Skinnemoen {
840be60a902SHaavard Skinnemoen 	flash_sect_t sector;
841be60a902SHaavard Skinnemoen 	int cnt;
842be60a902SHaavard Skinnemoen 	int retcode;
843cdbaefb5SHaavard Skinnemoen 	void *src = cp;
84412d30aa7SHaavard Skinnemoen 	void *dst = map_physmem(dest, len, MAP_NOCACHE);
8450dc80e27SStefan Roese 	void *dst2 = dst;
8460dc80e27SStefan Roese 	int flag = 0;
847cdbaefb5SHaavard Skinnemoen 
8480dc80e27SStefan Roese 	switch (info->portwidth) {
8490dc80e27SStefan Roese 	case FLASH_CFI_8BIT:
8500dc80e27SStefan Roese 		cnt = len;
8510dc80e27SStefan Roese 		break;
8520dc80e27SStefan Roese 	case FLASH_CFI_16BIT:
8530dc80e27SStefan Roese 		cnt = len >> 1;
8540dc80e27SStefan Roese 		break;
8550dc80e27SStefan Roese 	case FLASH_CFI_32BIT:
8560dc80e27SStefan Roese 		cnt = len >> 2;
8570dc80e27SStefan Roese 		break;
8580dc80e27SStefan Roese 	case FLASH_CFI_64BIT:
8590dc80e27SStefan Roese 		cnt = len >> 3;
8600dc80e27SStefan Roese 		break;
8610dc80e27SStefan Roese 	default:
8620dc80e27SStefan Roese 		retcode = ERR_INVAL;
8630dc80e27SStefan Roese 		goto out_unmap;
8640dc80e27SStefan Roese 	}
8650dc80e27SStefan Roese 
8660dc80e27SStefan Roese 	while ((cnt-- > 0) && (flag == 0)) {
8670dc80e27SStefan Roese 		switch (info->portwidth) {
8680dc80e27SStefan Roese 		case FLASH_CFI_8BIT:
8690dc80e27SStefan Roese 			flag = ((flash_read8(dst2) & flash_read8(src)) ==
8700dc80e27SStefan Roese 				flash_read8(src));
8710dc80e27SStefan Roese 			src += 1, dst2 += 1;
8720dc80e27SStefan Roese 			break;
8730dc80e27SStefan Roese 		case FLASH_CFI_16BIT:
8740dc80e27SStefan Roese 			flag = ((flash_read16(dst2) & flash_read16(src)) ==
8750dc80e27SStefan Roese 				flash_read16(src));
8760dc80e27SStefan Roese 			src += 2, dst2 += 2;
8770dc80e27SStefan Roese 			break;
8780dc80e27SStefan Roese 		case FLASH_CFI_32BIT:
8790dc80e27SStefan Roese 			flag = ((flash_read32(dst2) & flash_read32(src)) ==
8800dc80e27SStefan Roese 				flash_read32(src));
8810dc80e27SStefan Roese 			src += 4, dst2 += 4;
8820dc80e27SStefan Roese 			break;
8830dc80e27SStefan Roese 		case FLASH_CFI_64BIT:
8840dc80e27SStefan Roese 			flag = ((flash_read64(dst2) & flash_read64(src)) ==
8850dc80e27SStefan Roese 				flash_read64(src));
8860dc80e27SStefan Roese 			src += 8, dst2 += 8;
8870dc80e27SStefan Roese 			break;
8880dc80e27SStefan Roese 		}
8890dc80e27SStefan Roese 	}
8900dc80e27SStefan Roese 	if (!flag) {
8910dc80e27SStefan Roese 		retcode = ERR_NOT_ERASED;
8920dc80e27SStefan Roese 		goto out_unmap;
8930dc80e27SStefan Roese 	}
8940dc80e27SStefan Roese 
8950dc80e27SStefan Roese 	src = cp;
896cdbaefb5SHaavard Skinnemoen 	sector = find_sector (info, dest);
897be60a902SHaavard Skinnemoen 
898be60a902SHaavard Skinnemoen 	switch (info->vendor) {
899be60a902SHaavard Skinnemoen 	case CFI_CMDSET_INTEL_STANDARD:
900be60a902SHaavard Skinnemoen 	case CFI_CMDSET_INTEL_EXTENDED:
901be60a902SHaavard Skinnemoen 		flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS);
902be60a902SHaavard Skinnemoen 		flash_write_cmd (info, sector, 0, FLASH_CMD_WRITE_TO_BUFFER);
903be60a902SHaavard Skinnemoen 		retcode = flash_status_check (info, sector,
904be60a902SHaavard Skinnemoen 					      info->buffer_write_tout,
905be60a902SHaavard Skinnemoen 					      "write to buffer");
906be60a902SHaavard Skinnemoen 		if (retcode == ERR_OK) {
907be60a902SHaavard Skinnemoen 			/* reduce the number of loops by the width of
908be60a902SHaavard Skinnemoen 			 * the port */
909be60a902SHaavard Skinnemoen 			switch (info->portwidth) {
910be60a902SHaavard Skinnemoen 			case FLASH_CFI_8BIT:
911be60a902SHaavard Skinnemoen 				cnt = len;
912be60a902SHaavard Skinnemoen 				break;
913be60a902SHaavard Skinnemoen 			case FLASH_CFI_16BIT:
914be60a902SHaavard Skinnemoen 				cnt = len >> 1;
915be60a902SHaavard Skinnemoen 				break;
916be60a902SHaavard Skinnemoen 			case FLASH_CFI_32BIT:
917be60a902SHaavard Skinnemoen 				cnt = len >> 2;
918be60a902SHaavard Skinnemoen 				break;
919be60a902SHaavard Skinnemoen 			case FLASH_CFI_64BIT:
920be60a902SHaavard Skinnemoen 				cnt = len >> 3;
921be60a902SHaavard Skinnemoen 				break;
922be60a902SHaavard Skinnemoen 			default:
92312d30aa7SHaavard Skinnemoen 				retcode = ERR_INVAL;
92412d30aa7SHaavard Skinnemoen 				goto out_unmap;
925be60a902SHaavard Skinnemoen 			}
926be60a902SHaavard Skinnemoen 			flash_write_cmd (info, sector, 0, (uchar) cnt - 1);
927be60a902SHaavard Skinnemoen 			while (cnt-- > 0) {
928be60a902SHaavard Skinnemoen 				switch (info->portwidth) {
929be60a902SHaavard Skinnemoen 				case FLASH_CFI_8BIT:
930cdbaefb5SHaavard Skinnemoen 					flash_write8(flash_read8(src), dst);
931cdbaefb5SHaavard Skinnemoen 					src += 1, dst += 1;
932be60a902SHaavard Skinnemoen 					break;
933be60a902SHaavard Skinnemoen 				case FLASH_CFI_16BIT:
934cdbaefb5SHaavard Skinnemoen 					flash_write16(flash_read16(src), dst);
935cdbaefb5SHaavard Skinnemoen 					src += 2, dst += 2;
936be60a902SHaavard Skinnemoen 					break;
937be60a902SHaavard Skinnemoen 				case FLASH_CFI_32BIT:
938cdbaefb5SHaavard Skinnemoen 					flash_write32(flash_read32(src), dst);
939cdbaefb5SHaavard Skinnemoen 					src += 4, dst += 4;
940be60a902SHaavard Skinnemoen 					break;
941be60a902SHaavard Skinnemoen 				case FLASH_CFI_64BIT:
942cdbaefb5SHaavard Skinnemoen 					flash_write64(flash_read64(src), dst);
943cdbaefb5SHaavard Skinnemoen 					src += 8, dst += 8;
944be60a902SHaavard Skinnemoen 					break;
945be60a902SHaavard Skinnemoen 				default:
94612d30aa7SHaavard Skinnemoen 					retcode = ERR_INVAL;
94712d30aa7SHaavard Skinnemoen 					goto out_unmap;
948be60a902SHaavard Skinnemoen 				}
949be60a902SHaavard Skinnemoen 			}
950be60a902SHaavard Skinnemoen 			flash_write_cmd (info, sector, 0,
951be60a902SHaavard Skinnemoen 					 FLASH_CMD_WRITE_BUFFER_CONFIRM);
952be60a902SHaavard Skinnemoen 			retcode = flash_full_status_check (
953be60a902SHaavard Skinnemoen 				info, sector, info->buffer_write_tout,
954be60a902SHaavard Skinnemoen 				"buffer write");
955be60a902SHaavard Skinnemoen 		}
95612d30aa7SHaavard Skinnemoen 
95712d30aa7SHaavard Skinnemoen 		break;
958be60a902SHaavard Skinnemoen 
959be60a902SHaavard Skinnemoen 	case CFI_CMDSET_AMD_STANDARD:
960be60a902SHaavard Skinnemoen 	case CFI_CMDSET_AMD_EXTENDED:
961be60a902SHaavard Skinnemoen 		flash_unlock_seq(info,0);
962be60a902SHaavard Skinnemoen 		flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_TO_BUFFER);
963be60a902SHaavard Skinnemoen 
964be60a902SHaavard Skinnemoen 		switch (info->portwidth) {
965be60a902SHaavard Skinnemoen 		case FLASH_CFI_8BIT:
966be60a902SHaavard Skinnemoen 			cnt = len;
967be60a902SHaavard Skinnemoen 			flash_write_cmd (info, sector, 0,  (uchar) cnt - 1);
968cdbaefb5SHaavard Skinnemoen 			while (cnt-- > 0) {
969cdbaefb5SHaavard Skinnemoen 				flash_write8(flash_read8(src), dst);
970cdbaefb5SHaavard Skinnemoen 				src += 1, dst += 1;
971cdbaefb5SHaavard Skinnemoen 			}
972be60a902SHaavard Skinnemoen 			break;
973be60a902SHaavard Skinnemoen 		case FLASH_CFI_16BIT:
974be60a902SHaavard Skinnemoen 			cnt = len >> 1;
975be60a902SHaavard Skinnemoen 			flash_write_cmd (info, sector, 0,  (uchar) cnt - 1);
976cdbaefb5SHaavard Skinnemoen 			while (cnt-- > 0) {
977cdbaefb5SHaavard Skinnemoen 				flash_write16(flash_read16(src), dst);
978cdbaefb5SHaavard Skinnemoen 				src += 2, dst += 2;
979cdbaefb5SHaavard Skinnemoen 			}
980be60a902SHaavard Skinnemoen 			break;
981be60a902SHaavard Skinnemoen 		case FLASH_CFI_32BIT:
982be60a902SHaavard Skinnemoen 			cnt = len >> 2;
983be60a902SHaavard Skinnemoen 			flash_write_cmd (info, sector, 0,  (uchar) cnt - 1);
984cdbaefb5SHaavard Skinnemoen 			while (cnt-- > 0) {
985cdbaefb5SHaavard Skinnemoen 				flash_write32(flash_read32(src), dst);
986cdbaefb5SHaavard Skinnemoen 				src += 4, dst += 4;
987cdbaefb5SHaavard Skinnemoen 			}
988be60a902SHaavard Skinnemoen 			break;
989be60a902SHaavard Skinnemoen 		case FLASH_CFI_64BIT:
990be60a902SHaavard Skinnemoen 			cnt = len >> 3;
991be60a902SHaavard Skinnemoen 			flash_write_cmd (info, sector, 0,  (uchar) cnt - 1);
992cdbaefb5SHaavard Skinnemoen 			while (cnt-- > 0) {
993cdbaefb5SHaavard Skinnemoen 				flash_write64(flash_read64(src), dst);
994cdbaefb5SHaavard Skinnemoen 				src += 8, dst += 8;
995cdbaefb5SHaavard Skinnemoen 			}
996be60a902SHaavard Skinnemoen 			break;
997be60a902SHaavard Skinnemoen 		default:
99812d30aa7SHaavard Skinnemoen 			retcode = ERR_INVAL;
99912d30aa7SHaavard Skinnemoen 			goto out_unmap;
1000be60a902SHaavard Skinnemoen 		}
1001be60a902SHaavard Skinnemoen 
1002be60a902SHaavard Skinnemoen 		flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM);
1003be60a902SHaavard Skinnemoen 		retcode = flash_full_status_check (info, sector,
1004be60a902SHaavard Skinnemoen 						   info->buffer_write_tout,
1005be60a902SHaavard Skinnemoen 						   "buffer write");
100612d30aa7SHaavard Skinnemoen 		break;
1007be60a902SHaavard Skinnemoen 
1008be60a902SHaavard Skinnemoen 	default:
1009be60a902SHaavard Skinnemoen 		debug ("Unknown Command Set\n");
101012d30aa7SHaavard Skinnemoen 		retcode = ERR_INVAL;
101112d30aa7SHaavard Skinnemoen 		break;
1012be60a902SHaavard Skinnemoen 	}
101312d30aa7SHaavard Skinnemoen 
101412d30aa7SHaavard Skinnemoen out_unmap:
101512d30aa7SHaavard Skinnemoen 	unmap_physmem(dst, len);
101612d30aa7SHaavard Skinnemoen 	return retcode;
1017be60a902SHaavard Skinnemoen }
1018be60a902SHaavard Skinnemoen #endif /* CFG_FLASH_USE_BUFFER_WRITE */
1019be60a902SHaavard Skinnemoen 
102059829cc1SJean-Christophe PLAGNIOL-VILLARD 
102159829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
102259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
102359829cc1SJean-Christophe PLAGNIOL-VILLARD int flash_erase (flash_info_t * info, int s_first, int s_last)
102459829cc1SJean-Christophe PLAGNIOL-VILLARD {
102559829cc1SJean-Christophe PLAGNIOL-VILLARD 	int rcode = 0;
102659829cc1SJean-Christophe PLAGNIOL-VILLARD 	int prot;
102759829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_sect_t sect;
102859829cc1SJean-Christophe PLAGNIOL-VILLARD 
102959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (info->flash_id != FLASH_MAN_CFI) {
103059829cc1SJean-Christophe PLAGNIOL-VILLARD 		puts ("Can't erase unknown flash type - aborted\n");
103159829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 1;
103259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
103359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if ((s_first < 0) || (s_first > s_last)) {
103459829cc1SJean-Christophe PLAGNIOL-VILLARD 		puts ("- no sectors to erase\n");
103559829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 1;
103659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
103759829cc1SJean-Christophe PLAGNIOL-VILLARD 
103859829cc1SJean-Christophe PLAGNIOL-VILLARD 	prot = 0;
103959829cc1SJean-Christophe PLAGNIOL-VILLARD 	for (sect = s_first; sect <= s_last; ++sect) {
104059829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (info->protect[sect]) {
104159829cc1SJean-Christophe PLAGNIOL-VILLARD 			prot++;
104259829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
104359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
104459829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (prot) {
10457e5b9b47SHaavard Skinnemoen 		printf ("- Warning: %d protected sectors will not be erased!\n",
10467e5b9b47SHaavard Skinnemoen 			prot);
104759829cc1SJean-Christophe PLAGNIOL-VILLARD 	} else {
104859829cc1SJean-Christophe PLAGNIOL-VILLARD 		putc ('\n');
104959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
105059829cc1SJean-Christophe PLAGNIOL-VILLARD 
105159829cc1SJean-Christophe PLAGNIOL-VILLARD 
105259829cc1SJean-Christophe PLAGNIOL-VILLARD 	for (sect = s_first; sect <= s_last; sect++) {
105359829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (info->protect[sect] == 0) { /* not protected */
105459829cc1SJean-Christophe PLAGNIOL-VILLARD 			switch (info->vendor) {
105559829cc1SJean-Christophe PLAGNIOL-VILLARD 			case CFI_CMDSET_INTEL_STANDARD:
105659829cc1SJean-Christophe PLAGNIOL-VILLARD 			case CFI_CMDSET_INTEL_EXTENDED:
10577e5b9b47SHaavard Skinnemoen 				flash_write_cmd (info, sect, 0,
10587e5b9b47SHaavard Skinnemoen 						 FLASH_CMD_CLEAR_STATUS);
10597e5b9b47SHaavard Skinnemoen 				flash_write_cmd (info, sect, 0,
10607e5b9b47SHaavard Skinnemoen 						 FLASH_CMD_BLOCK_ERASE);
10617e5b9b47SHaavard Skinnemoen 				flash_write_cmd (info, sect, 0,
10627e5b9b47SHaavard Skinnemoen 						 FLASH_CMD_ERASE_CONFIRM);
106359829cc1SJean-Christophe PLAGNIOL-VILLARD 				break;
106459829cc1SJean-Christophe PLAGNIOL-VILLARD 			case CFI_CMDSET_AMD_STANDARD:
106559829cc1SJean-Christophe PLAGNIOL-VILLARD 			case CFI_CMDSET_AMD_EXTENDED:
106659829cc1SJean-Christophe PLAGNIOL-VILLARD 				flash_unlock_seq (info, sect);
10677e5b9b47SHaavard Skinnemoen 				flash_write_cmd (info, sect,
10687e5b9b47SHaavard Skinnemoen 						info->addr_unlock1,
10697e5b9b47SHaavard Skinnemoen 						AMD_CMD_ERASE_START);
107059829cc1SJean-Christophe PLAGNIOL-VILLARD 				flash_unlock_seq (info, sect);
10717e5b9b47SHaavard Skinnemoen 				flash_write_cmd (info, sect, 0,
10727e5b9b47SHaavard Skinnemoen 						 AMD_CMD_ERASE_SECTOR);
107359829cc1SJean-Christophe PLAGNIOL-VILLARD 				break;
107481b20cccSMichael Schwingen #ifdef CONFIG_FLASH_CFI_LEGACY
107581b20cccSMichael Schwingen 			case CFI_CMDSET_AMD_LEGACY:
107681b20cccSMichael Schwingen 				flash_unlock_seq (info, 0);
10777e5b9b47SHaavard Skinnemoen 				flash_write_cmd (info, 0, info->addr_unlock1,
10787e5b9b47SHaavard Skinnemoen 						AMD_CMD_ERASE_START);
107981b20cccSMichael Schwingen 				flash_unlock_seq (info, 0);
10807e5b9b47SHaavard Skinnemoen 				flash_write_cmd (info, sect, 0,
10817e5b9b47SHaavard Skinnemoen 						AMD_CMD_ERASE_SECTOR);
108281b20cccSMichael Schwingen 				break;
108381b20cccSMichael Schwingen #endif
108459829cc1SJean-Christophe PLAGNIOL-VILLARD 			default:
108559829cc1SJean-Christophe PLAGNIOL-VILLARD 				debug ("Unkown flash vendor %d\n",
108659829cc1SJean-Christophe PLAGNIOL-VILLARD 				       info->vendor);
108759829cc1SJean-Christophe PLAGNIOL-VILLARD 				break;
108859829cc1SJean-Christophe PLAGNIOL-VILLARD 			}
108959829cc1SJean-Christophe PLAGNIOL-VILLARD 
109059829cc1SJean-Christophe PLAGNIOL-VILLARD 			if (flash_full_status_check
109159829cc1SJean-Christophe PLAGNIOL-VILLARD 			    (info, sect, info->erase_blk_tout, "erase")) {
109259829cc1SJean-Christophe PLAGNIOL-VILLARD 				rcode = 1;
109359829cc1SJean-Christophe PLAGNIOL-VILLARD 			} else
109459829cc1SJean-Christophe PLAGNIOL-VILLARD 				putc ('.');
109559829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
109659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
109759829cc1SJean-Christophe PLAGNIOL-VILLARD 	puts (" done\n");
109859829cc1SJean-Christophe PLAGNIOL-VILLARD 	return rcode;
109959829cc1SJean-Christophe PLAGNIOL-VILLARD }
110059829cc1SJean-Christophe PLAGNIOL-VILLARD 
110159829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
110259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
110359829cc1SJean-Christophe PLAGNIOL-VILLARD void flash_print_info (flash_info_t * info)
110459829cc1SJean-Christophe PLAGNIOL-VILLARD {
110559829cc1SJean-Christophe PLAGNIOL-VILLARD 	int i;
110659829cc1SJean-Christophe PLAGNIOL-VILLARD 
110759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (info->flash_id != FLASH_MAN_CFI) {
110859829cc1SJean-Christophe PLAGNIOL-VILLARD 		puts ("missing or unknown FLASH type\n");
110959829cc1SJean-Christophe PLAGNIOL-VILLARD 		return;
111059829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
111159829cc1SJean-Christophe PLAGNIOL-VILLARD 
111281b20cccSMichael Schwingen 	printf ("%s FLASH (%d x %d)",
111381b20cccSMichael Schwingen 		info->name,
111459829cc1SJean-Christophe PLAGNIOL-VILLARD 		(info->portwidth << 3), (info->chipwidth << 3));
111581b20cccSMichael Schwingen 	if (info->size < 1024*1024)
111681b20cccSMichael Schwingen 		printf ("  Size: %ld kB in %d Sectors\n",
111781b20cccSMichael Schwingen 			info->size >> 10, info->sector_count);
111881b20cccSMichael Schwingen 	else
111959829cc1SJean-Christophe PLAGNIOL-VILLARD 		printf ("  Size: %ld MB in %d Sectors\n",
112059829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->size >> 20, info->sector_count);
112159829cc1SJean-Christophe PLAGNIOL-VILLARD 	printf ("  ");
112259829cc1SJean-Christophe PLAGNIOL-VILLARD 	switch (info->vendor) {
112359829cc1SJean-Christophe PLAGNIOL-VILLARD 		case CFI_CMDSET_INTEL_STANDARD:
112459829cc1SJean-Christophe PLAGNIOL-VILLARD 			printf ("Intel Standard");
112559829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
112659829cc1SJean-Christophe PLAGNIOL-VILLARD 		case CFI_CMDSET_INTEL_EXTENDED:
112759829cc1SJean-Christophe PLAGNIOL-VILLARD 			printf ("Intel Extended");
112859829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
112959829cc1SJean-Christophe PLAGNIOL-VILLARD 		case CFI_CMDSET_AMD_STANDARD:
113059829cc1SJean-Christophe PLAGNIOL-VILLARD 			printf ("AMD Standard");
113159829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
113259829cc1SJean-Christophe PLAGNIOL-VILLARD 		case CFI_CMDSET_AMD_EXTENDED:
113359829cc1SJean-Christophe PLAGNIOL-VILLARD 			printf ("AMD Extended");
113459829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
113581b20cccSMichael Schwingen #ifdef CONFIG_FLASH_CFI_LEGACY
113681b20cccSMichael Schwingen 		case CFI_CMDSET_AMD_LEGACY:
113781b20cccSMichael Schwingen 			printf ("AMD Legacy");
113881b20cccSMichael Schwingen 			break;
113981b20cccSMichael Schwingen #endif
114059829cc1SJean-Christophe PLAGNIOL-VILLARD 		default:
114159829cc1SJean-Christophe PLAGNIOL-VILLARD 			printf ("Unknown (%d)", info->vendor);
114259829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
114359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
114459829cc1SJean-Christophe PLAGNIOL-VILLARD 	printf (" command set, Manufacturer ID: 0x%02X, Device ID: 0x%02X",
114559829cc1SJean-Christophe PLAGNIOL-VILLARD 		info->manufacturer_id, info->device_id);
114659829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (info->device_id == 0x7E) {
114759829cc1SJean-Christophe PLAGNIOL-VILLARD 		printf("%04X", info->device_id2);
114859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
114959829cc1SJean-Christophe PLAGNIOL-VILLARD 	printf ("\n  Erase timeout: %ld ms, write timeout: %ld ms\n",
115059829cc1SJean-Christophe PLAGNIOL-VILLARD 		info->erase_blk_tout,
115159829cc1SJean-Christophe PLAGNIOL-VILLARD 		info->write_tout);
115259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (info->buffer_size > 1) {
11537e5b9b47SHaavard Skinnemoen 		printf ("  Buffer write timeout: %ld ms, "
11547e5b9b47SHaavard Skinnemoen 			"buffer size: %d bytes\n",
115559829cc1SJean-Christophe PLAGNIOL-VILLARD 		info->buffer_write_tout,
115659829cc1SJean-Christophe PLAGNIOL-VILLARD 		info->buffer_size);
115759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
115859829cc1SJean-Christophe PLAGNIOL-VILLARD 
115959829cc1SJean-Christophe PLAGNIOL-VILLARD 	puts ("\n  Sector Start Addresses:");
116059829cc1SJean-Christophe PLAGNIOL-VILLARD 	for (i = 0; i < info->sector_count; ++i) {
116159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if ((i % 5) == 0)
116259829cc1SJean-Christophe PLAGNIOL-VILLARD 			printf ("\n");
116359829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CFG_FLASH_EMPTY_INFO
116459829cc1SJean-Christophe PLAGNIOL-VILLARD 		int k;
116559829cc1SJean-Christophe PLAGNIOL-VILLARD 		int size;
116659829cc1SJean-Christophe PLAGNIOL-VILLARD 		int erased;
116759829cc1SJean-Christophe PLAGNIOL-VILLARD 		volatile unsigned long *flash;
116859829cc1SJean-Christophe PLAGNIOL-VILLARD 
116959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/*
117059829cc1SJean-Christophe PLAGNIOL-VILLARD 		 * Check if whole sector is erased
117159829cc1SJean-Christophe PLAGNIOL-VILLARD 		 */
117212d30aa7SHaavard Skinnemoen 		size = flash_sector_size(info, i);
117359829cc1SJean-Christophe PLAGNIOL-VILLARD 		erased = 1;
117459829cc1SJean-Christophe PLAGNIOL-VILLARD 		flash = (volatile unsigned long *) info->start[i];
117559829cc1SJean-Christophe PLAGNIOL-VILLARD 		size = size >> 2;	/* divide by 4 for longword access */
117659829cc1SJean-Christophe PLAGNIOL-VILLARD 		for (k = 0; k < size; k++) {
117759829cc1SJean-Christophe PLAGNIOL-VILLARD 			if (*flash++ != 0xffffffff) {
117859829cc1SJean-Christophe PLAGNIOL-VILLARD 				erased = 0;
117959829cc1SJean-Christophe PLAGNIOL-VILLARD 				break;
118059829cc1SJean-Christophe PLAGNIOL-VILLARD 			}
118159829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
118259829cc1SJean-Christophe PLAGNIOL-VILLARD 
118359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* print empty and read-only info */
118459829cc1SJean-Christophe PLAGNIOL-VILLARD 		printf ("  %08lX %c %s ",
118559829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->start[i],
118659829cc1SJean-Christophe PLAGNIOL-VILLARD 			erased ? 'E' : ' ',
118759829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->protect[i] ? "RO" : "  ");
118859829cc1SJean-Christophe PLAGNIOL-VILLARD #else	/* ! CFG_FLASH_EMPTY_INFO */
118959829cc1SJean-Christophe PLAGNIOL-VILLARD 		printf ("  %08lX   %s ",
119059829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->start[i],
119159829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->protect[i] ? "RO" : "  ");
119259829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
119359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
119459829cc1SJean-Christophe PLAGNIOL-VILLARD 	putc ('\n');
119559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return;
119659829cc1SJean-Christophe PLAGNIOL-VILLARD }
119759829cc1SJean-Christophe PLAGNIOL-VILLARD 
119859829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
11999a042e9cSJerry Van Baren  * This is used in a few places in write_buf() to show programming
12009a042e9cSJerry Van Baren  * progress.  Making it a function is nasty because it needs to do side
12019a042e9cSJerry Van Baren  * effect updates to digit and dots.  Repeated code is nasty too, so
12029a042e9cSJerry Van Baren  * we define it once here.
12039a042e9cSJerry Van Baren  */
1204f0105727SStefan Roese #ifdef CONFIG_FLASH_SHOW_PROGRESS
1205f0105727SStefan Roese #define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub) \
1206f0105727SStefan Roese 	dots -= dots_sub; \
12079a042e9cSJerry Van Baren 	if ((scale > 0) && (dots <= 0)) { \
12089a042e9cSJerry Van Baren 		if ((digit % 5) == 0) \
12099a042e9cSJerry Van Baren 			printf ("%d", digit / 5); \
12109a042e9cSJerry Van Baren 		else \
12119a042e9cSJerry Van Baren 			putc ('.'); \
12129a042e9cSJerry Van Baren 		digit--; \
12139a042e9cSJerry Van Baren 		dots += scale; \
12149a042e9cSJerry Van Baren 	}
1215f0105727SStefan Roese #else
1216f0105727SStefan Roese #define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub)
1217f0105727SStefan Roese #endif
12189a042e9cSJerry Van Baren 
12199a042e9cSJerry Van Baren /*-----------------------------------------------------------------------
122059829cc1SJean-Christophe PLAGNIOL-VILLARD  * Copy memory to flash, returns:
122159829cc1SJean-Christophe PLAGNIOL-VILLARD  * 0 - OK
122259829cc1SJean-Christophe PLAGNIOL-VILLARD  * 1 - write timeout
122359829cc1SJean-Christophe PLAGNIOL-VILLARD  * 2 - Flash not erased
122459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
122559829cc1SJean-Christophe PLAGNIOL-VILLARD int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
122659829cc1SJean-Christophe PLAGNIOL-VILLARD {
122759829cc1SJean-Christophe PLAGNIOL-VILLARD 	ulong wp;
122812d30aa7SHaavard Skinnemoen 	uchar *p;
122959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int aln;
123059829cc1SJean-Christophe PLAGNIOL-VILLARD 	cfiword_t cword;
123159829cc1SJean-Christophe PLAGNIOL-VILLARD 	int i, rc;
123259829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CFG_FLASH_USE_BUFFER_WRITE
123359829cc1SJean-Christophe PLAGNIOL-VILLARD 	int buffered_size;
123459829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
12359a042e9cSJerry Van Baren #ifdef CONFIG_FLASH_SHOW_PROGRESS
12369a042e9cSJerry Van Baren 	int digit = CONFIG_FLASH_SHOW_PROGRESS;
12379a042e9cSJerry Van Baren 	int scale = 0;
12389a042e9cSJerry Van Baren 	int dots  = 0;
12399a042e9cSJerry Van Baren 
12409a042e9cSJerry Van Baren 	/*
12419a042e9cSJerry Van Baren 	 * Suppress if there are fewer than CONFIG_FLASH_SHOW_PROGRESS writes.
12429a042e9cSJerry Van Baren 	 */
12439a042e9cSJerry Van Baren 	if (cnt >= CONFIG_FLASH_SHOW_PROGRESS) {
12449a042e9cSJerry Van Baren 		scale = (int)((cnt + CONFIG_FLASH_SHOW_PROGRESS - 1) /
12459a042e9cSJerry Van Baren 			CONFIG_FLASH_SHOW_PROGRESS);
12469a042e9cSJerry Van Baren 	}
12479a042e9cSJerry Van Baren #endif
12489a042e9cSJerry Van Baren 
124959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* get lower aligned address */
125059829cc1SJean-Christophe PLAGNIOL-VILLARD 	wp = (addr & ~(info->portwidth - 1));
125159829cc1SJean-Christophe PLAGNIOL-VILLARD 
125259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* handle unaligned start */
125359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if ((aln = addr - wp) != 0) {
125459829cc1SJean-Christophe PLAGNIOL-VILLARD 		cword.l = 0;
125512d30aa7SHaavard Skinnemoen 		p = map_physmem(wp, info->portwidth, MAP_NOCACHE);
125612d30aa7SHaavard Skinnemoen 		for (i = 0; i < aln; ++i)
125712d30aa7SHaavard Skinnemoen 			flash_add_byte (info, &cword, flash_read8(p + i));
125859829cc1SJean-Christophe PLAGNIOL-VILLARD 
125959829cc1SJean-Christophe PLAGNIOL-VILLARD 		for (; (i < info->portwidth) && (cnt > 0); i++) {
126059829cc1SJean-Christophe PLAGNIOL-VILLARD 			flash_add_byte (info, &cword, *src++);
126159829cc1SJean-Christophe PLAGNIOL-VILLARD 			cnt--;
126259829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
126312d30aa7SHaavard Skinnemoen 		for (; (cnt == 0) && (i < info->portwidth); ++i)
126412d30aa7SHaavard Skinnemoen 			flash_add_byte (info, &cword, flash_read8(p + i));
126512d30aa7SHaavard Skinnemoen 
126612d30aa7SHaavard Skinnemoen 		rc = flash_write_cfiword (info, wp, cword);
126712d30aa7SHaavard Skinnemoen 		unmap_physmem(p, info->portwidth);
126812d30aa7SHaavard Skinnemoen 		if (rc != 0)
126959829cc1SJean-Christophe PLAGNIOL-VILLARD 			return rc;
127012d30aa7SHaavard Skinnemoen 
127112d30aa7SHaavard Skinnemoen 		wp += i;
1272f0105727SStefan Roese 		FLASH_SHOW_PROGRESS(scale, dots, digit, i);
127359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
127459829cc1SJean-Christophe PLAGNIOL-VILLARD 
127559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* handle the aligned part */
127659829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CFG_FLASH_USE_BUFFER_WRITE
127759829cc1SJean-Christophe PLAGNIOL-VILLARD 	buffered_size = (info->portwidth / info->chipwidth);
127859829cc1SJean-Christophe PLAGNIOL-VILLARD 	buffered_size *= info->buffer_size;
127959829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (cnt >= info->portwidth) {
128059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* prohibit buffer write when buffer_size is 1 */
128159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (info->buffer_size == 1) {
128259829cc1SJean-Christophe PLAGNIOL-VILLARD 			cword.l = 0;
128359829cc1SJean-Christophe PLAGNIOL-VILLARD 			for (i = 0; i < info->portwidth; i++)
128459829cc1SJean-Christophe PLAGNIOL-VILLARD 				flash_add_byte (info, &cword, *src++);
128559829cc1SJean-Christophe PLAGNIOL-VILLARD 			if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
128659829cc1SJean-Christophe PLAGNIOL-VILLARD 				return rc;
128759829cc1SJean-Christophe PLAGNIOL-VILLARD 			wp += info->portwidth;
128859829cc1SJean-Christophe PLAGNIOL-VILLARD 			cnt -= info->portwidth;
128959829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
129059829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
129159829cc1SJean-Christophe PLAGNIOL-VILLARD 
129259829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* write buffer until next buffered_size aligned boundary */
129359829cc1SJean-Christophe PLAGNIOL-VILLARD 		i = buffered_size - (wp % buffered_size);
129459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (i > cnt)
129559829cc1SJean-Christophe PLAGNIOL-VILLARD 			i = cnt;
129659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK)
129759829cc1SJean-Christophe PLAGNIOL-VILLARD 			return rc;
129859829cc1SJean-Christophe PLAGNIOL-VILLARD 		i -= i & (info->portwidth - 1);
129959829cc1SJean-Christophe PLAGNIOL-VILLARD 		wp += i;
130059829cc1SJean-Christophe PLAGNIOL-VILLARD 		src += i;
130159829cc1SJean-Christophe PLAGNIOL-VILLARD 		cnt -= i;
1302f0105727SStefan Roese 		FLASH_SHOW_PROGRESS(scale, dots, digit, i);
130359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
130459829cc1SJean-Christophe PLAGNIOL-VILLARD #else
130559829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (cnt >= info->portwidth) {
130659829cc1SJean-Christophe PLAGNIOL-VILLARD 		cword.l = 0;
130759829cc1SJean-Christophe PLAGNIOL-VILLARD 		for (i = 0; i < info->portwidth; i++) {
130859829cc1SJean-Christophe PLAGNIOL-VILLARD 			flash_add_byte (info, &cword, *src++);
130959829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
131059829cc1SJean-Christophe PLAGNIOL-VILLARD 		if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
131159829cc1SJean-Christophe PLAGNIOL-VILLARD 			return rc;
131259829cc1SJean-Christophe PLAGNIOL-VILLARD 		wp += info->portwidth;
131359829cc1SJean-Christophe PLAGNIOL-VILLARD 		cnt -= info->portwidth;
1314f0105727SStefan Roese 		FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth);
131559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
131659829cc1SJean-Christophe PLAGNIOL-VILLARD #endif /* CFG_FLASH_USE_BUFFER_WRITE */
13179a042e9cSJerry Van Baren 
131859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (cnt == 0) {
131959829cc1SJean-Christophe PLAGNIOL-VILLARD 		return (0);
132059829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
132159829cc1SJean-Christophe PLAGNIOL-VILLARD 
132259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/*
132359829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * handle unaligned tail bytes
132459829cc1SJean-Christophe PLAGNIOL-VILLARD 	 */
132559829cc1SJean-Christophe PLAGNIOL-VILLARD 	cword.l = 0;
132612d30aa7SHaavard Skinnemoen 	p = map_physmem(wp, info->portwidth, MAP_NOCACHE);
132712d30aa7SHaavard Skinnemoen 	for (i = 0; (i < info->portwidth) && (cnt > 0); ++i) {
132859829cc1SJean-Christophe PLAGNIOL-VILLARD 		flash_add_byte (info, &cword, *src++);
132959829cc1SJean-Christophe PLAGNIOL-VILLARD 		--cnt;
133059829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
133112d30aa7SHaavard Skinnemoen 	for (; i < info->portwidth; ++i)
133212d30aa7SHaavard Skinnemoen 		flash_add_byte (info, &cword, flash_read8(p + i));
133312d30aa7SHaavard Skinnemoen 	unmap_physmem(p, info->portwidth);
133459829cc1SJean-Christophe PLAGNIOL-VILLARD 
133559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return flash_write_cfiword (info, wp, cword);
133659829cc1SJean-Christophe PLAGNIOL-VILLARD }
133759829cc1SJean-Christophe PLAGNIOL-VILLARD 
133859829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
133959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
134059829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CFG_FLASH_PROTECTION
134159829cc1SJean-Christophe PLAGNIOL-VILLARD 
134259829cc1SJean-Christophe PLAGNIOL-VILLARD int flash_real_protect (flash_info_t * info, long sector, int prot)
134359829cc1SJean-Christophe PLAGNIOL-VILLARD {
134459829cc1SJean-Christophe PLAGNIOL-VILLARD 	int retcode = 0;
134559829cc1SJean-Christophe PLAGNIOL-VILLARD 
134659829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS);
134759829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT);
134859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (prot)
134959829cc1SJean-Christophe PLAGNIOL-VILLARD 		flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_SET);
135059829cc1SJean-Christophe PLAGNIOL-VILLARD 	else
135159829cc1SJean-Christophe PLAGNIOL-VILLARD 		flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_CLEAR);
135259829cc1SJean-Christophe PLAGNIOL-VILLARD 
135359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if ((retcode =
135459829cc1SJean-Christophe PLAGNIOL-VILLARD 	     flash_full_status_check (info, sector, info->erase_blk_tout,
135559829cc1SJean-Christophe PLAGNIOL-VILLARD 				      prot ? "protect" : "unprotect")) == 0) {
135659829cc1SJean-Christophe PLAGNIOL-VILLARD 
135759829cc1SJean-Christophe PLAGNIOL-VILLARD 		info->protect[sector] = prot;
135859829cc1SJean-Christophe PLAGNIOL-VILLARD 
135959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/*
136059829cc1SJean-Christophe PLAGNIOL-VILLARD 		 * On some of Intel's flash chips (marked via legacy_unlock)
136159829cc1SJean-Christophe PLAGNIOL-VILLARD 		 * unprotect unprotects all locking.
136259829cc1SJean-Christophe PLAGNIOL-VILLARD 		 */
136359829cc1SJean-Christophe PLAGNIOL-VILLARD 		if ((prot == 0) && (info->legacy_unlock)) {
136459829cc1SJean-Christophe PLAGNIOL-VILLARD 			flash_sect_t i;
136559829cc1SJean-Christophe PLAGNIOL-VILLARD 
136659829cc1SJean-Christophe PLAGNIOL-VILLARD 			for (i = 0; i < info->sector_count; i++) {
136759829cc1SJean-Christophe PLAGNIOL-VILLARD 				if (info->protect[i])
136859829cc1SJean-Christophe PLAGNIOL-VILLARD 					flash_real_protect (info, i, 1);
136959829cc1SJean-Christophe PLAGNIOL-VILLARD 			}
137059829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
137159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
137259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return retcode;
137359829cc1SJean-Christophe PLAGNIOL-VILLARD }
137459829cc1SJean-Christophe PLAGNIOL-VILLARD 
137559829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
137659829cc1SJean-Christophe PLAGNIOL-VILLARD  * flash_read_user_serial - read the OneTimeProgramming cells
137759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
137859829cc1SJean-Christophe PLAGNIOL-VILLARD void flash_read_user_serial (flash_info_t * info, void *buffer, int offset,
137959829cc1SJean-Christophe PLAGNIOL-VILLARD 			     int len)
138059829cc1SJean-Christophe PLAGNIOL-VILLARD {
138159829cc1SJean-Christophe PLAGNIOL-VILLARD 	uchar *src;
138259829cc1SJean-Christophe PLAGNIOL-VILLARD 	uchar *dst;
138359829cc1SJean-Christophe PLAGNIOL-VILLARD 
138459829cc1SJean-Christophe PLAGNIOL-VILLARD 	dst = buffer;
138512d30aa7SHaavard Skinnemoen 	src = flash_map (info, 0, FLASH_OFFSET_USER_PROTECTION);
138659829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
138759829cc1SJean-Christophe PLAGNIOL-VILLARD 	memcpy (dst, src + offset, len);
138859829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd (info, 0, 0, info->cmd_reset);
138912d30aa7SHaavard Skinnemoen 	flash_unmap(info, 0, FLASH_OFFSET_USER_PROTECTION, src);
139059829cc1SJean-Christophe PLAGNIOL-VILLARD }
139159829cc1SJean-Christophe PLAGNIOL-VILLARD 
139259829cc1SJean-Christophe PLAGNIOL-VILLARD /*
139359829cc1SJean-Christophe PLAGNIOL-VILLARD  * flash_read_factory_serial - read the device Id from the protection area
139459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
139559829cc1SJean-Christophe PLAGNIOL-VILLARD void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset,
139659829cc1SJean-Christophe PLAGNIOL-VILLARD 				int len)
139759829cc1SJean-Christophe PLAGNIOL-VILLARD {
139859829cc1SJean-Christophe PLAGNIOL-VILLARD 	uchar *src;
139959829cc1SJean-Christophe PLAGNIOL-VILLARD 
140012d30aa7SHaavard Skinnemoen 	src = flash_map (info, 0, FLASH_OFFSET_INTEL_PROTECTION);
140159829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
140259829cc1SJean-Christophe PLAGNIOL-VILLARD 	memcpy (buffer, src + offset, len);
140359829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd (info, 0, 0, info->cmd_reset);
140412d30aa7SHaavard Skinnemoen 	flash_unmap(info, 0, FLASH_OFFSET_INTEL_PROTECTION, src);
140559829cc1SJean-Christophe PLAGNIOL-VILLARD }
140659829cc1SJean-Christophe PLAGNIOL-VILLARD 
140759829cc1SJean-Christophe PLAGNIOL-VILLARD #endif /* CFG_FLASH_PROTECTION */
140859829cc1SJean-Christophe PLAGNIOL-VILLARD 
14090ddf06ddSHaavard Skinnemoen /*-----------------------------------------------------------------------
14100ddf06ddSHaavard Skinnemoen  * Reverse the order of the erase regions in the CFI QRY structure.
14110ddf06ddSHaavard Skinnemoen  * This is needed for chips that are either a) correctly detected as
14120ddf06ddSHaavard Skinnemoen  * top-boot, or b) buggy.
14130ddf06ddSHaavard Skinnemoen  */
14140ddf06ddSHaavard Skinnemoen static void cfi_reverse_geometry(struct cfi_qry *qry)
14150ddf06ddSHaavard Skinnemoen {
14160ddf06ddSHaavard Skinnemoen 	unsigned int i, j;
14170ddf06ddSHaavard Skinnemoen 	u32 tmp;
14180ddf06ddSHaavard Skinnemoen 
14190ddf06ddSHaavard Skinnemoen 	for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) {
14200ddf06ddSHaavard Skinnemoen 		tmp = qry->erase_region_info[i];
14210ddf06ddSHaavard Skinnemoen 		qry->erase_region_info[i] = qry->erase_region_info[j];
14220ddf06ddSHaavard Skinnemoen 		qry->erase_region_info[j] = tmp;
14230ddf06ddSHaavard Skinnemoen 	}
14240ddf06ddSHaavard Skinnemoen }
142559829cc1SJean-Christophe PLAGNIOL-VILLARD 
142659829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
142759829cc1SJean-Christophe PLAGNIOL-VILLARD  * read jedec ids from device and set corresponding fields in info struct
142859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
142959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct
143059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
143159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
14320ddf06ddSHaavard Skinnemoen static void cmdset_intel_read_jedec_ids(flash_info_t *info)
143359829cc1SJean-Christophe PLAGNIOL-VILLARD {
143459829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
143559829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID);
143659829cc1SJean-Christophe PLAGNIOL-VILLARD 	udelay(1000); /* some flash are slow to respond */
143759829cc1SJean-Christophe PLAGNIOL-VILLARD 	info->manufacturer_id = flash_read_uchar (info,
143859829cc1SJean-Christophe PLAGNIOL-VILLARD 					FLASH_OFFSET_MANUFACTURER_ID);
143959829cc1SJean-Christophe PLAGNIOL-VILLARD 	info->device_id = flash_read_uchar (info,
144059829cc1SJean-Christophe PLAGNIOL-VILLARD 					FLASH_OFFSET_DEVICE_ID);
144159829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
14420ddf06ddSHaavard Skinnemoen }
14430ddf06ddSHaavard Skinnemoen 
14440ddf06ddSHaavard Skinnemoen static int cmdset_intel_init(flash_info_t *info, struct cfi_qry *qry)
14450ddf06ddSHaavard Skinnemoen {
14460ddf06ddSHaavard Skinnemoen 	info->cmd_reset = FLASH_CMD_RESET;
14470ddf06ddSHaavard Skinnemoen 
14480ddf06ddSHaavard Skinnemoen 	cmdset_intel_read_jedec_ids(info);
14490ddf06ddSHaavard Skinnemoen 	flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI);
14500ddf06ddSHaavard Skinnemoen 
14510ddf06ddSHaavard Skinnemoen #ifdef CFG_FLASH_PROTECTION
14520ddf06ddSHaavard Skinnemoen 	/* read legacy lock/unlock bit from intel flash */
14530ddf06ddSHaavard Skinnemoen 	if (info->ext_addr) {
14540ddf06ddSHaavard Skinnemoen 		info->legacy_unlock = flash_read_uchar (info,
14550ddf06ddSHaavard Skinnemoen 				info->ext_addr + 5) & 0x08;
14560ddf06ddSHaavard Skinnemoen 	}
14570ddf06ddSHaavard Skinnemoen #endif
14580ddf06ddSHaavard Skinnemoen 
14590ddf06ddSHaavard Skinnemoen 	return 0;
14600ddf06ddSHaavard Skinnemoen }
14610ddf06ddSHaavard Skinnemoen 
14620ddf06ddSHaavard Skinnemoen static void cmdset_amd_read_jedec_ids(flash_info_t *info)
14630ddf06ddSHaavard Skinnemoen {
146459829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd(info, 0, 0, AMD_CMD_RESET);
146559829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_unlock_seq(info, 0);
146681b20cccSMichael Schwingen 	flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID);
146759829cc1SJean-Christophe PLAGNIOL-VILLARD 	udelay(1000); /* some flash are slow to respond */
146890447ecbSTor Krill 
146959829cc1SJean-Christophe PLAGNIOL-VILLARD 	info->manufacturer_id = flash_read_uchar (info,
147059829cc1SJean-Christophe PLAGNIOL-VILLARD 					FLASH_OFFSET_MANUFACTURER_ID);
147190447ecbSTor Krill 
147290447ecbSTor Krill 	switch (info->chipwidth){
147390447ecbSTor Krill 	case FLASH_CFI_8BIT:
147459829cc1SJean-Christophe PLAGNIOL-VILLARD 		info->device_id = flash_read_uchar (info,
147559829cc1SJean-Christophe PLAGNIOL-VILLARD 						FLASH_OFFSET_DEVICE_ID);
147659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (info->device_id == 0x7E) {
147759829cc1SJean-Christophe PLAGNIOL-VILLARD 			/* AMD 3-byte (expanded) device ids */
147859829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->device_id2 = flash_read_uchar (info,
147959829cc1SJean-Christophe PLAGNIOL-VILLARD 						FLASH_OFFSET_DEVICE_ID2);
148059829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->device_id2 <<= 8;
148159829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->device_id2 |= flash_read_uchar (info,
148259829cc1SJean-Christophe PLAGNIOL-VILLARD 						FLASH_OFFSET_DEVICE_ID3);
148359829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
148490447ecbSTor Krill 		break;
148590447ecbSTor Krill 	case FLASH_CFI_16BIT:
148690447ecbSTor Krill 		info->device_id = flash_read_word (info,
148790447ecbSTor Krill 						FLASH_OFFSET_DEVICE_ID);
148890447ecbSTor Krill 		break;
148990447ecbSTor Krill 	default:
149090447ecbSTor Krill 		break;
149190447ecbSTor Krill 	}
149259829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd(info, 0, 0, AMD_CMD_RESET);
14930ddf06ddSHaavard Skinnemoen }
14940ddf06ddSHaavard Skinnemoen 
14950ddf06ddSHaavard Skinnemoen static int cmdset_amd_init(flash_info_t *info, struct cfi_qry *qry)
14960ddf06ddSHaavard Skinnemoen {
14970ddf06ddSHaavard Skinnemoen 	info->cmd_reset = AMD_CMD_RESET;
14980ddf06ddSHaavard Skinnemoen 
14990ddf06ddSHaavard Skinnemoen 	cmdset_amd_read_jedec_ids(info);
15000ddf06ddSHaavard Skinnemoen 	flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI);
15010ddf06ddSHaavard Skinnemoen 
15020ddf06ddSHaavard Skinnemoen 	return 0;
15030ddf06ddSHaavard Skinnemoen }
15040ddf06ddSHaavard Skinnemoen 
15050ddf06ddSHaavard Skinnemoen #ifdef CONFIG_FLASH_CFI_LEGACY
15060ddf06ddSHaavard Skinnemoen static void flash_read_jedec_ids (flash_info_t * info)
15070ddf06ddSHaavard Skinnemoen {
15080ddf06ddSHaavard Skinnemoen 	info->manufacturer_id = 0;
15090ddf06ddSHaavard Skinnemoen 	info->device_id       = 0;
15100ddf06ddSHaavard Skinnemoen 	info->device_id2      = 0;
15110ddf06ddSHaavard Skinnemoen 
15120ddf06ddSHaavard Skinnemoen 	switch (info->vendor) {
15130ddf06ddSHaavard Skinnemoen 	case CFI_CMDSET_INTEL_STANDARD:
15140ddf06ddSHaavard Skinnemoen 	case CFI_CMDSET_INTEL_EXTENDED:
15158225d1e3SMichael Schwingen 		cmdset_intel_read_jedec_ids(info);
15160ddf06ddSHaavard Skinnemoen 		break;
15170ddf06ddSHaavard Skinnemoen 	case CFI_CMDSET_AMD_STANDARD:
15180ddf06ddSHaavard Skinnemoen 	case CFI_CMDSET_AMD_EXTENDED:
15198225d1e3SMichael Schwingen 		cmdset_amd_read_jedec_ids(info);
152059829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
152159829cc1SJean-Christophe PLAGNIOL-VILLARD 	default:
152259829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
152359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
152459829cc1SJean-Christophe PLAGNIOL-VILLARD }
152559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1526be60a902SHaavard Skinnemoen /*-----------------------------------------------------------------------
1527be60a902SHaavard Skinnemoen  * Call board code to request info about non-CFI flash.
1528be60a902SHaavard Skinnemoen  * board_flash_get_legacy needs to fill in at least:
1529be60a902SHaavard Skinnemoen  * info->portwidth, info->chipwidth and info->interface for Jedec probing.
1530be60a902SHaavard Skinnemoen  */
1531be60a902SHaavard Skinnemoen static int flash_detect_legacy(ulong base, int banknum)
1532be60a902SHaavard Skinnemoen {
1533be60a902SHaavard Skinnemoen 	flash_info_t *info = &flash_info[banknum];
1534be60a902SHaavard Skinnemoen 
1535be60a902SHaavard Skinnemoen 	if (board_flash_get_legacy(base, banknum, info)) {
1536be60a902SHaavard Skinnemoen 		/* board code may have filled info completely. If not, we
1537be60a902SHaavard Skinnemoen 		   use JEDEC ID probing. */
1538be60a902SHaavard Skinnemoen 		if (!info->vendor) {
1539be60a902SHaavard Skinnemoen 			int modes[] = {
1540be60a902SHaavard Skinnemoen 				CFI_CMDSET_AMD_STANDARD,
1541be60a902SHaavard Skinnemoen 				CFI_CMDSET_INTEL_STANDARD
1542be60a902SHaavard Skinnemoen 			};
1543be60a902SHaavard Skinnemoen 			int i;
1544be60a902SHaavard Skinnemoen 
1545be60a902SHaavard Skinnemoen 			for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) {
1546be60a902SHaavard Skinnemoen 				info->vendor = modes[i];
1547be60a902SHaavard Skinnemoen 				info->start[0] = base;
1548be60a902SHaavard Skinnemoen 				if (info->portwidth == FLASH_CFI_8BIT
1549be60a902SHaavard Skinnemoen 					&& info->interface == FLASH_CFI_X8X16) {
1550be60a902SHaavard Skinnemoen 					info->addr_unlock1 = 0x2AAA;
1551be60a902SHaavard Skinnemoen 					info->addr_unlock2 = 0x5555;
1552be60a902SHaavard Skinnemoen 				} else {
1553be60a902SHaavard Skinnemoen 					info->addr_unlock1 = 0x5555;
1554be60a902SHaavard Skinnemoen 					info->addr_unlock2 = 0x2AAA;
1555be60a902SHaavard Skinnemoen 				}
1556be60a902SHaavard Skinnemoen 				flash_read_jedec_ids(info);
1557be60a902SHaavard Skinnemoen 				debug("JEDEC PROBE: ID %x %x %x\n",
1558be60a902SHaavard Skinnemoen 						info->manufacturer_id,
1559be60a902SHaavard Skinnemoen 						info->device_id,
1560be60a902SHaavard Skinnemoen 						info->device_id2);
1561be60a902SHaavard Skinnemoen 				if (jedec_flash_match(info, base))
1562be60a902SHaavard Skinnemoen 					break;
1563be60a902SHaavard Skinnemoen 			}
1564be60a902SHaavard Skinnemoen 		}
1565be60a902SHaavard Skinnemoen 
1566be60a902SHaavard Skinnemoen 		switch(info->vendor) {
1567be60a902SHaavard Skinnemoen 		case CFI_CMDSET_INTEL_STANDARD:
1568be60a902SHaavard Skinnemoen 		case CFI_CMDSET_INTEL_EXTENDED:
1569be60a902SHaavard Skinnemoen 			info->cmd_reset = FLASH_CMD_RESET;
1570be60a902SHaavard Skinnemoen 			break;
1571be60a902SHaavard Skinnemoen 		case CFI_CMDSET_AMD_STANDARD:
1572be60a902SHaavard Skinnemoen 		case CFI_CMDSET_AMD_EXTENDED:
1573be60a902SHaavard Skinnemoen 		case CFI_CMDSET_AMD_LEGACY:
1574be60a902SHaavard Skinnemoen 			info->cmd_reset = AMD_CMD_RESET;
1575be60a902SHaavard Skinnemoen 			break;
1576be60a902SHaavard Skinnemoen 		}
1577be60a902SHaavard Skinnemoen 		info->flash_id = FLASH_MAN_CFI;
1578be60a902SHaavard Skinnemoen 		return 1;
1579be60a902SHaavard Skinnemoen 	}
1580be60a902SHaavard Skinnemoen 	return 0; /* use CFI */
1581be60a902SHaavard Skinnemoen }
1582be60a902SHaavard Skinnemoen #else
1583be60a902SHaavard Skinnemoen static inline int flash_detect_legacy(ulong base, int banknum)
1584be60a902SHaavard Skinnemoen {
1585be60a902SHaavard Skinnemoen 	return 0; /* use CFI */
1586be60a902SHaavard Skinnemoen }
1587be60a902SHaavard Skinnemoen #endif
1588be60a902SHaavard Skinnemoen 
158959829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
159059829cc1SJean-Christophe PLAGNIOL-VILLARD  * detect if flash is compatible with the Common Flash Interface (CFI)
159159829cc1SJean-Christophe PLAGNIOL-VILLARD  * http://www.jedec.org/download/search/jesd68.pdf
159259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1593e23741f4SHaavard Skinnemoen static void flash_read_cfi (flash_info_t *info, void *buf,
1594e23741f4SHaavard Skinnemoen 		unsigned int start, size_t len)
1595e23741f4SHaavard Skinnemoen {
1596e23741f4SHaavard Skinnemoen 	u8 *p = buf;
1597e23741f4SHaavard Skinnemoen 	unsigned int i;
1598e23741f4SHaavard Skinnemoen 
1599e23741f4SHaavard Skinnemoen 	for (i = 0; i < len; i++)
1600e23741f4SHaavard Skinnemoen 		p[i] = flash_read_uchar(info, start + i);
1601e23741f4SHaavard Skinnemoen }
1602e23741f4SHaavard Skinnemoen 
1603e23741f4SHaavard Skinnemoen static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
160459829cc1SJean-Christophe PLAGNIOL-VILLARD {
160559829cc1SJean-Christophe PLAGNIOL-VILLARD 	int cfi_offset;
160659829cc1SJean-Christophe PLAGNIOL-VILLARD 
16071ba639daSMichael Schwingen 	/* We do not yet know what kind of commandset to use, so we issue
16081ba639daSMichael Schwingen 	   the reset command in both Intel and AMD variants, in the hope
16091ba639daSMichael Schwingen 	   that AMD flash roms ignore the Intel command. */
16101ba639daSMichael Schwingen 	flash_write_cmd (info, 0, 0, AMD_CMD_RESET);
16111ba639daSMichael Schwingen 	flash_write_cmd (info, 0, 0, FLASH_CMD_RESET);
16121ba639daSMichael Schwingen 
16137e5b9b47SHaavard Skinnemoen 	for (cfi_offset=0;
16147e5b9b47SHaavard Skinnemoen 	     cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint);
16157e5b9b47SHaavard Skinnemoen 	     cfi_offset++) {
16167e5b9b47SHaavard Skinnemoen 		flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],
16177e5b9b47SHaavard Skinnemoen 				 FLASH_CMD_CFI);
161859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q')
161959829cc1SJean-Christophe PLAGNIOL-VILLARD 		    && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R')
162059829cc1SJean-Christophe PLAGNIOL-VILLARD 		    && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) {
1621e23741f4SHaavard Skinnemoen 			flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,
1622e23741f4SHaavard Skinnemoen 					sizeof(struct cfi_qry));
1623e23741f4SHaavard Skinnemoen 			info->interface	= le16_to_cpu(qry->interface_desc);
1624e23741f4SHaavard Skinnemoen 
162559829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->cfi_offset = flash_offset_cfi[cfi_offset];
162659829cc1SJean-Christophe PLAGNIOL-VILLARD 			debug ("device interface is %d\n",
162759829cc1SJean-Christophe PLAGNIOL-VILLARD 			       info->interface);
162859829cc1SJean-Christophe PLAGNIOL-VILLARD 			debug ("found port %d chip %d ",
162959829cc1SJean-Christophe PLAGNIOL-VILLARD 			       info->portwidth, info->chipwidth);
163059829cc1SJean-Christophe PLAGNIOL-VILLARD 			debug ("port %d bits chip %d bits\n",
163159829cc1SJean-Christophe PLAGNIOL-VILLARD 			       info->portwidth << CFI_FLASH_SHIFT_WIDTH,
163259829cc1SJean-Christophe PLAGNIOL-VILLARD 			       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
163342026c9cSBartlomiej Sieka 
163442026c9cSBartlomiej Sieka 			/* calculate command offsets as in the Linux driver */
163542026c9cSBartlomiej Sieka 			info->addr_unlock1 = 0x555;
163642026c9cSBartlomiej Sieka 			info->addr_unlock2 = 0x2aa;
163742026c9cSBartlomiej Sieka 
163842026c9cSBartlomiej Sieka 			/*
163942026c9cSBartlomiej Sieka 			 * modify the unlock address if we are
164042026c9cSBartlomiej Sieka 			 * in compatibility mode
164142026c9cSBartlomiej Sieka 			 */
164242026c9cSBartlomiej Sieka 			if (	/* x8/x16 in x8 mode */
164342026c9cSBartlomiej Sieka 				((info->chipwidth == FLASH_CFI_BY8) &&
164442026c9cSBartlomiej Sieka 					(info->interface == FLASH_CFI_X8X16)) ||
164542026c9cSBartlomiej Sieka 				/* x16/x32 in x16 mode */
164642026c9cSBartlomiej Sieka 				((info->chipwidth == FLASH_CFI_BY16) &&
164742026c9cSBartlomiej Sieka 					(info->interface == FLASH_CFI_X16X32)))
164842026c9cSBartlomiej Sieka 			{
164942026c9cSBartlomiej Sieka 				info->addr_unlock1 = 0xaaa;
165042026c9cSBartlomiej Sieka 				info->addr_unlock2 = 0x555;
165142026c9cSBartlomiej Sieka 			}
165242026c9cSBartlomiej Sieka 
165381b20cccSMichael Schwingen 			info->name = "CFI conformant";
165459829cc1SJean-Christophe PLAGNIOL-VILLARD 			return 1;
165559829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
165659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
16577e5b9b47SHaavard Skinnemoen 
16587e5b9b47SHaavard Skinnemoen 	return 0;
165959829cc1SJean-Christophe PLAGNIOL-VILLARD }
16607e5b9b47SHaavard Skinnemoen 
1661e23741f4SHaavard Skinnemoen static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
16627e5b9b47SHaavard Skinnemoen {
16637e5b9b47SHaavard Skinnemoen 	debug ("flash detect cfi\n");
16647e5b9b47SHaavard Skinnemoen 
16657e5b9b47SHaavard Skinnemoen 	for (info->portwidth = CFG_FLASH_CFI_WIDTH;
16667e5b9b47SHaavard Skinnemoen 	     info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) {
16677e5b9b47SHaavard Skinnemoen 		for (info->chipwidth = FLASH_CFI_BY8;
16687e5b9b47SHaavard Skinnemoen 		     info->chipwidth <= info->portwidth;
16697e5b9b47SHaavard Skinnemoen 		     info->chipwidth <<= 1)
1670e23741f4SHaavard Skinnemoen 			if (__flash_detect_cfi(info, qry))
16717e5b9b47SHaavard Skinnemoen 				return 1;
167259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
167359829cc1SJean-Christophe PLAGNIOL-VILLARD 	debug ("not found\n");
167459829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
167559829cc1SJean-Christophe PLAGNIOL-VILLARD }
167659829cc1SJean-Christophe PLAGNIOL-VILLARD 
167759829cc1SJean-Christophe PLAGNIOL-VILLARD /*
1678467bcee1SHaavard Skinnemoen  * Manufacturer-specific quirks. Add workarounds for geometry
1679467bcee1SHaavard Skinnemoen  * reversal, etc. here.
1680467bcee1SHaavard Skinnemoen  */
1681467bcee1SHaavard Skinnemoen static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry)
1682467bcee1SHaavard Skinnemoen {
1683467bcee1SHaavard Skinnemoen 	/* check if flash geometry needs reversal */
1684467bcee1SHaavard Skinnemoen 	if (qry->num_erase_regions > 1) {
1685467bcee1SHaavard Skinnemoen 		/* reverse geometry if top boot part */
1686467bcee1SHaavard Skinnemoen 		if (info->cfi_version < 0x3131) {
1687467bcee1SHaavard Skinnemoen 			/* CFI < 1.1, try to guess from device id */
1688467bcee1SHaavard Skinnemoen 			if ((info->device_id & 0x80) != 0)
1689467bcee1SHaavard Skinnemoen 				cfi_reverse_geometry(qry);
1690467bcee1SHaavard Skinnemoen 		} else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) {
1691467bcee1SHaavard Skinnemoen 			/* CFI >= 1.1, deduct from top/bottom flag */
1692467bcee1SHaavard Skinnemoen 			/* note: ext_addr is valid since cfi_version > 0 */
1693467bcee1SHaavard Skinnemoen 			cfi_reverse_geometry(qry);
1694467bcee1SHaavard Skinnemoen 		}
1695467bcee1SHaavard Skinnemoen 	}
1696467bcee1SHaavard Skinnemoen }
1697467bcee1SHaavard Skinnemoen 
1698467bcee1SHaavard Skinnemoen static void flash_fixup_atmel(flash_info_t *info, struct cfi_qry *qry)
1699467bcee1SHaavard Skinnemoen {
1700467bcee1SHaavard Skinnemoen 	int reverse_geometry = 0;
1701467bcee1SHaavard Skinnemoen 
1702467bcee1SHaavard Skinnemoen 	/* Check the "top boot" bit in the PRI */
1703467bcee1SHaavard Skinnemoen 	if (info->ext_addr && !(flash_read_uchar(info, info->ext_addr + 6) & 1))
1704467bcee1SHaavard Skinnemoen 		reverse_geometry = 1;
1705467bcee1SHaavard Skinnemoen 
1706467bcee1SHaavard Skinnemoen 	/* AT49BV6416(T) list the erase regions in the wrong order.
1707467bcee1SHaavard Skinnemoen 	 * However, the device ID is identical with the non-broken
1708467bcee1SHaavard Skinnemoen 	 * AT49BV642D since u-boot only reads the low byte (they
1709467bcee1SHaavard Skinnemoen 	 * differ in the high byte.) So leave out this fixup for now.
1710467bcee1SHaavard Skinnemoen 	 */
1711467bcee1SHaavard Skinnemoen #if 0
1712467bcee1SHaavard Skinnemoen 	if (info->device_id == 0xd6 || info->device_id == 0xd2)
1713467bcee1SHaavard Skinnemoen 		reverse_geometry = !reverse_geometry;
1714467bcee1SHaavard Skinnemoen #endif
1715467bcee1SHaavard Skinnemoen 
1716467bcee1SHaavard Skinnemoen 	if (reverse_geometry)
1717467bcee1SHaavard Skinnemoen 		cfi_reverse_geometry(qry);
1718467bcee1SHaavard Skinnemoen }
1719467bcee1SHaavard Skinnemoen 
1720467bcee1SHaavard Skinnemoen /*
172159829cc1SJean-Christophe PLAGNIOL-VILLARD  * The following code cannot be run from FLASH!
172259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
172359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
172459829cc1SJean-Christophe PLAGNIOL-VILLARD ulong flash_get_size (ulong base, int banknum)
172559829cc1SJean-Christophe PLAGNIOL-VILLARD {
172659829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_info_t *info = &flash_info[banknum];
172759829cc1SJean-Christophe PLAGNIOL-VILLARD 	int i, j;
172859829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_sect_t sect_cnt;
172959829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned long sector;
173059829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned long tmp;
173159829cc1SJean-Christophe PLAGNIOL-VILLARD 	int size_ratio;
173259829cc1SJean-Christophe PLAGNIOL-VILLARD 	uchar num_erase_regions;
173359829cc1SJean-Christophe PLAGNIOL-VILLARD 	int erase_region_size;
173459829cc1SJean-Christophe PLAGNIOL-VILLARD 	int erase_region_count;
1735e23741f4SHaavard Skinnemoen 	struct cfi_qry qry;
173659829cc1SJean-Christophe PLAGNIOL-VILLARD 
173759829cc1SJean-Christophe PLAGNIOL-VILLARD 	info->ext_addr = 0;
173859829cc1SJean-Christophe PLAGNIOL-VILLARD 	info->cfi_version = 0;
173959829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CFG_FLASH_PROTECTION
174059829cc1SJean-Christophe PLAGNIOL-VILLARD 	info->legacy_unlock = 0;
174159829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
174259829cc1SJean-Christophe PLAGNIOL-VILLARD 
174359829cc1SJean-Christophe PLAGNIOL-VILLARD 	info->start[0] = base;
174459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1745e23741f4SHaavard Skinnemoen 	if (flash_detect_cfi (info, &qry)) {
1746e23741f4SHaavard Skinnemoen 		info->vendor = le16_to_cpu(qry.p_id);
1747e23741f4SHaavard Skinnemoen 		info->ext_addr = le16_to_cpu(qry.p_adr);
1748e23741f4SHaavard Skinnemoen 		num_erase_regions = qry.num_erase_regions;
1749e23741f4SHaavard Skinnemoen 
175059829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (info->ext_addr) {
175159829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->cfi_version = (ushort) flash_read_uchar (info,
175259829cc1SJean-Christophe PLAGNIOL-VILLARD 						info->ext_addr + 3) << 8;
175359829cc1SJean-Christophe PLAGNIOL-VILLARD 			info->cfi_version |= (ushort) flash_read_uchar (info,
175459829cc1SJean-Christophe PLAGNIOL-VILLARD 						info->ext_addr + 4);
175559829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
17560ddf06ddSHaavard Skinnemoen 
175759829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG
1758e23741f4SHaavard Skinnemoen 		flash_printqry (&qry);
175959829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
17600ddf06ddSHaavard Skinnemoen 
176159829cc1SJean-Christophe PLAGNIOL-VILLARD 		switch (info->vendor) {
176259829cc1SJean-Christophe PLAGNIOL-VILLARD 		case CFI_CMDSET_INTEL_STANDARD:
176359829cc1SJean-Christophe PLAGNIOL-VILLARD 		case CFI_CMDSET_INTEL_EXTENDED:
17640ddf06ddSHaavard Skinnemoen 			cmdset_intel_init(info, &qry);
176559829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
176659829cc1SJean-Christophe PLAGNIOL-VILLARD 		case CFI_CMDSET_AMD_STANDARD:
176759829cc1SJean-Christophe PLAGNIOL-VILLARD 		case CFI_CMDSET_AMD_EXTENDED:
17680ddf06ddSHaavard Skinnemoen 			cmdset_amd_init(info, &qry);
176959829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
17700ddf06ddSHaavard Skinnemoen 		default:
17710ddf06ddSHaavard Skinnemoen 			printf("CFI: Unknown command set 0x%x\n",
17720ddf06ddSHaavard Skinnemoen 					info->vendor);
17730ddf06ddSHaavard Skinnemoen 			/*
17740ddf06ddSHaavard Skinnemoen 			 * Unfortunately, this means we don't know how
17750ddf06ddSHaavard Skinnemoen 			 * to get the chip back to Read mode. Might
17760ddf06ddSHaavard Skinnemoen 			 * as well try an Intel-style reset...
17770ddf06ddSHaavard Skinnemoen 			 */
17780ddf06ddSHaavard Skinnemoen 			flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
17790ddf06ddSHaavard Skinnemoen 			return 0;
178059829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
178159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1782467bcee1SHaavard Skinnemoen 		/* Do manufacturer-specific fixups */
1783467bcee1SHaavard Skinnemoen 		switch (info->manufacturer_id) {
1784467bcee1SHaavard Skinnemoen 		case 0x0001:
1785467bcee1SHaavard Skinnemoen 			flash_fixup_amd(info, &qry);
1786467bcee1SHaavard Skinnemoen 			break;
1787467bcee1SHaavard Skinnemoen 		case 0x001f:
1788467bcee1SHaavard Skinnemoen 			flash_fixup_atmel(info, &qry);
1789467bcee1SHaavard Skinnemoen 			break;
1790467bcee1SHaavard Skinnemoen 		}
1791467bcee1SHaavard Skinnemoen 
179259829cc1SJean-Christophe PLAGNIOL-VILLARD 		debug ("manufacturer is %d\n", info->vendor);
179359829cc1SJean-Christophe PLAGNIOL-VILLARD 		debug ("manufacturer id is 0x%x\n", info->manufacturer_id);
179459829cc1SJean-Christophe PLAGNIOL-VILLARD 		debug ("device id is 0x%x\n", info->device_id);
179559829cc1SJean-Christophe PLAGNIOL-VILLARD 		debug ("device id2 is 0x%x\n", info->device_id2);
179659829cc1SJean-Christophe PLAGNIOL-VILLARD 		debug ("cfi version is 0x%04x\n", info->cfi_version);
179759829cc1SJean-Christophe PLAGNIOL-VILLARD 
179859829cc1SJean-Christophe PLAGNIOL-VILLARD 		size_ratio = info->portwidth / info->chipwidth;
179959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* if the chip is x8/x16 reduce the ratio by half */
180059829cc1SJean-Christophe PLAGNIOL-VILLARD 		if ((info->interface == FLASH_CFI_X8X16)
180159829cc1SJean-Christophe PLAGNIOL-VILLARD 		    && (info->chipwidth == FLASH_CFI_BY8)) {
180259829cc1SJean-Christophe PLAGNIOL-VILLARD 			size_ratio >>= 1;
180359829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
180459829cc1SJean-Christophe PLAGNIOL-VILLARD 		debug ("size_ratio %d port %d bits chip %d bits\n",
180559829cc1SJean-Christophe PLAGNIOL-VILLARD 		       size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH,
180659829cc1SJean-Christophe PLAGNIOL-VILLARD 		       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
180759829cc1SJean-Christophe PLAGNIOL-VILLARD 		debug ("found %d erase regions\n", num_erase_regions);
180859829cc1SJean-Christophe PLAGNIOL-VILLARD 		sect_cnt = 0;
180959829cc1SJean-Christophe PLAGNIOL-VILLARD 		sector = base;
181059829cc1SJean-Christophe PLAGNIOL-VILLARD 		for (i = 0; i < num_erase_regions; i++) {
181159829cc1SJean-Christophe PLAGNIOL-VILLARD 			if (i > NUM_ERASE_REGIONS) {
181259829cc1SJean-Christophe PLAGNIOL-VILLARD 				printf ("%d erase regions found, only %d used\n",
181359829cc1SJean-Christophe PLAGNIOL-VILLARD 					num_erase_regions, NUM_ERASE_REGIONS);
181459829cc1SJean-Christophe PLAGNIOL-VILLARD 				break;
181559829cc1SJean-Christophe PLAGNIOL-VILLARD 			}
1816e23741f4SHaavard Skinnemoen 
18170ddf06ddSHaavard Skinnemoen 			tmp = le32_to_cpu(qry.erase_region_info[i]);
18180ddf06ddSHaavard Skinnemoen 			debug("erase region %u: 0x%08lx\n", i, tmp);
1819e23741f4SHaavard Skinnemoen 
1820e23741f4SHaavard Skinnemoen 			erase_region_count = (tmp & 0xffff) + 1;
1821e23741f4SHaavard Skinnemoen 			tmp >>= 16;
182259829cc1SJean-Christophe PLAGNIOL-VILLARD 			erase_region_size =
182359829cc1SJean-Christophe PLAGNIOL-VILLARD 				(tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128;
182459829cc1SJean-Christophe PLAGNIOL-VILLARD 			debug ("erase_region_count = %d erase_region_size = %d\n",
182559829cc1SJean-Christophe PLAGNIOL-VILLARD 				erase_region_count, erase_region_size);
182659829cc1SJean-Christophe PLAGNIOL-VILLARD 			for (j = 0; j < erase_region_count; j++) {
182781b20cccSMichael Schwingen 				if (sect_cnt >= CFG_MAX_FLASH_SECT) {
182881b20cccSMichael Schwingen 					printf("ERROR: too many flash sectors\n");
182981b20cccSMichael Schwingen 					break;
183081b20cccSMichael Schwingen 				}
183159829cc1SJean-Christophe PLAGNIOL-VILLARD 				info->start[sect_cnt] = sector;
183259829cc1SJean-Christophe PLAGNIOL-VILLARD 				sector += (erase_region_size * size_ratio);
183359829cc1SJean-Christophe PLAGNIOL-VILLARD 
183459829cc1SJean-Christophe PLAGNIOL-VILLARD 				/*
18357e5b9b47SHaavard Skinnemoen 				 * Only read protection status from
18367e5b9b47SHaavard Skinnemoen 				 * supported devices (intel...)
183759829cc1SJean-Christophe PLAGNIOL-VILLARD 				 */
183859829cc1SJean-Christophe PLAGNIOL-VILLARD 				switch (info->vendor) {
183959829cc1SJean-Christophe PLAGNIOL-VILLARD 				case CFI_CMDSET_INTEL_EXTENDED:
184059829cc1SJean-Christophe PLAGNIOL-VILLARD 				case CFI_CMDSET_INTEL_STANDARD:
184159829cc1SJean-Christophe PLAGNIOL-VILLARD 					info->protect[sect_cnt] =
184259829cc1SJean-Christophe PLAGNIOL-VILLARD 						flash_isset (info, sect_cnt,
184359829cc1SJean-Christophe PLAGNIOL-VILLARD 							     FLASH_OFFSET_PROTECT,
184459829cc1SJean-Christophe PLAGNIOL-VILLARD 							     FLASH_STATUS_PROTECT);
184559829cc1SJean-Christophe PLAGNIOL-VILLARD 					break;
184659829cc1SJean-Christophe PLAGNIOL-VILLARD 				default:
18477e5b9b47SHaavard Skinnemoen 					/* default: not protected */
18487e5b9b47SHaavard Skinnemoen 					info->protect[sect_cnt] = 0;
184959829cc1SJean-Christophe PLAGNIOL-VILLARD 				}
185059829cc1SJean-Christophe PLAGNIOL-VILLARD 
185159829cc1SJean-Christophe PLAGNIOL-VILLARD 				sect_cnt++;
185259829cc1SJean-Christophe PLAGNIOL-VILLARD 			}
185359829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
185459829cc1SJean-Christophe PLAGNIOL-VILLARD 
185559829cc1SJean-Christophe PLAGNIOL-VILLARD 		info->sector_count = sect_cnt;
1856e23741f4SHaavard Skinnemoen 		info->size = 1 << qry.dev_size;
185759829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* multiply the size by the number of chips */
18587e5b9b47SHaavard Skinnemoen 		info->size *= size_ratio;
1859e23741f4SHaavard Skinnemoen 		info->buffer_size = 1 << le16_to_cpu(qry.max_buf_write_size);
1860e23741f4SHaavard Skinnemoen 		tmp = 1 << qry.block_erase_timeout_typ;
18617e5b9b47SHaavard Skinnemoen 		info->erase_blk_tout = tmp *
1862e23741f4SHaavard Skinnemoen 			(1 << qry.block_erase_timeout_max);
1863e23741f4SHaavard Skinnemoen 		tmp = (1 << qry.buf_write_timeout_typ) *
1864e23741f4SHaavard Skinnemoen 			(1 << qry.buf_write_timeout_max);
1865e23741f4SHaavard Skinnemoen 
18667e5b9b47SHaavard Skinnemoen 		/* round up when converting to ms */
1867e23741f4SHaavard Skinnemoen 		info->buffer_write_tout = (tmp + 999) / 1000;
1868e23741f4SHaavard Skinnemoen 		tmp = (1 << qry.word_write_timeout_typ) *
1869e23741f4SHaavard Skinnemoen 			(1 << qry.word_write_timeout_max);
18707e5b9b47SHaavard Skinnemoen 		/* round up when converting to ms */
1871e23741f4SHaavard Skinnemoen 		info->write_tout = (tmp + 999) / 1000;
187259829cc1SJean-Christophe PLAGNIOL-VILLARD 		info->flash_id = FLASH_MAN_CFI;
18737e5b9b47SHaavard Skinnemoen 		if ((info->interface == FLASH_CFI_X8X16) &&
18747e5b9b47SHaavard Skinnemoen 		    (info->chipwidth == FLASH_CFI_BY8)) {
18757e5b9b47SHaavard Skinnemoen 			/* XXX - Need to test on x8/x16 in parallel. */
18767e5b9b47SHaavard Skinnemoen 			info->portwidth >>= 1;
187759829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
187859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
187959829cc1SJean-Christophe PLAGNIOL-VILLARD 
188059829cc1SJean-Christophe PLAGNIOL-VILLARD 	flash_write_cmd (info, 0, 0, info->cmd_reset);
188159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return (info->size);
188259829cc1SJean-Christophe PLAGNIOL-VILLARD }
188359829cc1SJean-Christophe PLAGNIOL-VILLARD 
188459829cc1SJean-Christophe PLAGNIOL-VILLARD /*-----------------------------------------------------------------------
188559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1886be60a902SHaavard Skinnemoen unsigned long flash_init (void)
188759829cc1SJean-Christophe PLAGNIOL-VILLARD {
1888be60a902SHaavard Skinnemoen 	unsigned long size = 0;
1889be60a902SHaavard Skinnemoen 	int i;
189059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1891be60a902SHaavard Skinnemoen #ifdef CFG_FLASH_PROTECTION
1892be60a902SHaavard Skinnemoen 	char *s = getenv("unlock");
189381b20cccSMichael Schwingen #endif
1894be60a902SHaavard Skinnemoen 
1895be60a902SHaavard Skinnemoen 	/* Init: no FLASHes known */
1896be60a902SHaavard Skinnemoen 	for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) {
1897be60a902SHaavard Skinnemoen 		flash_info[i].flash_id = FLASH_UNKNOWN;
1898be60a902SHaavard Skinnemoen 
1899be60a902SHaavard Skinnemoen 		if (!flash_detect_legacy (bank_base[i], i))
1900be60a902SHaavard Skinnemoen 			flash_get_size (bank_base[i], i);
1901be60a902SHaavard Skinnemoen 		size += flash_info[i].size;
1902be60a902SHaavard Skinnemoen 		if (flash_info[i].flash_id == FLASH_UNKNOWN) {
1903be60a902SHaavard Skinnemoen #ifndef CFG_FLASH_QUIET_TEST
1904be60a902SHaavard Skinnemoen 			printf ("## Unknown FLASH on Bank %d "
1905be60a902SHaavard Skinnemoen 				"- Size = 0x%08lx = %ld MB\n",
1906be60a902SHaavard Skinnemoen 				i+1, flash_info[i].size,
1907be60a902SHaavard Skinnemoen 				flash_info[i].size << 20);
1908be60a902SHaavard Skinnemoen #endif /* CFG_FLASH_QUIET_TEST */
190959829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
1910be60a902SHaavard Skinnemoen #ifdef CFG_FLASH_PROTECTION
1911be60a902SHaavard Skinnemoen 		else if ((s != NULL) && (strcmp(s, "yes") == 0)) {
1912be60a902SHaavard Skinnemoen 			/*
1913be60a902SHaavard Skinnemoen 			 * Only the U-Boot image and it's environment
1914be60a902SHaavard Skinnemoen 			 * is protected, all other sectors are
1915be60a902SHaavard Skinnemoen 			 * unprotected (unlocked) if flash hardware
1916be60a902SHaavard Skinnemoen 			 * protection is used (CFG_FLASH_PROTECTION)
1917be60a902SHaavard Skinnemoen 			 * and the environment variable "unlock" is
1918be60a902SHaavard Skinnemoen 			 * set to "yes".
1919be60a902SHaavard Skinnemoen 			 */
1920be60a902SHaavard Skinnemoen 			if (flash_info[i].legacy_unlock) {
1921be60a902SHaavard Skinnemoen 				int k;
192259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1923be60a902SHaavard Skinnemoen 				/*
1924be60a902SHaavard Skinnemoen 				 * Disable legacy_unlock temporarily,
1925be60a902SHaavard Skinnemoen 				 * since flash_real_protect would
1926be60a902SHaavard Skinnemoen 				 * relock all other sectors again
1927be60a902SHaavard Skinnemoen 				 * otherwise.
1928be60a902SHaavard Skinnemoen 				 */
1929be60a902SHaavard Skinnemoen 				flash_info[i].legacy_unlock = 0;
193059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1931be60a902SHaavard Skinnemoen 				/*
1932be60a902SHaavard Skinnemoen 				 * Legacy unlocking (e.g. Intel J3) ->
1933be60a902SHaavard Skinnemoen 				 * unlock only one sector. This will
1934be60a902SHaavard Skinnemoen 				 * unlock all sectors.
1935be60a902SHaavard Skinnemoen 				 */
1936be60a902SHaavard Skinnemoen 				flash_real_protect (&flash_info[i], 0, 0);
193759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1938be60a902SHaavard Skinnemoen 				flash_info[i].legacy_unlock = 1;
193959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1940be60a902SHaavard Skinnemoen 				/*
1941be60a902SHaavard Skinnemoen 				 * Manually mark other sectors as
1942be60a902SHaavard Skinnemoen 				 * unlocked (unprotected)
1943be60a902SHaavard Skinnemoen 				 */
1944be60a902SHaavard Skinnemoen 				for (k = 1; k < flash_info[i].sector_count; k++)
1945be60a902SHaavard Skinnemoen 					flash_info[i].protect[k] = 0;
1946be60a902SHaavard Skinnemoen 			} else {
1947be60a902SHaavard Skinnemoen 				/*
1948be60a902SHaavard Skinnemoen 				 * No legancy unlocking -> unlock all sectors
1949be60a902SHaavard Skinnemoen 				 */
1950be60a902SHaavard Skinnemoen 				flash_protect (FLAG_PROTECT_CLEAR,
1951be60a902SHaavard Skinnemoen 					       flash_info[i].start[0],
1952be60a902SHaavard Skinnemoen 					       flash_info[i].start[0]
1953be60a902SHaavard Skinnemoen 					       + flash_info[i].size - 1,
1954be60a902SHaavard Skinnemoen 					       &flash_info[i]);
195559829cc1SJean-Christophe PLAGNIOL-VILLARD 			}
195659829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
1957be60a902SHaavard Skinnemoen #endif /* CFG_FLASH_PROTECTION */
195859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
195959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1960be60a902SHaavard Skinnemoen 	/* Monitor protection ON by default */
1961be60a902SHaavard Skinnemoen #if (CFG_MONITOR_BASE >= CFG_FLASH_BASE)
1962be60a902SHaavard Skinnemoen 	flash_protect (FLAG_PROTECT_SET,
1963be60a902SHaavard Skinnemoen 		       CFG_MONITOR_BASE,
1964be60a902SHaavard Skinnemoen 		       CFG_MONITOR_BASE + monitor_flash_len  - 1,
1965be60a902SHaavard Skinnemoen 		       flash_get_info(CFG_MONITOR_BASE));
1966be60a902SHaavard Skinnemoen #endif
196759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1968be60a902SHaavard Skinnemoen 	/* Environment protection ON by default */
1969be60a902SHaavard Skinnemoen #ifdef CFG_ENV_IS_IN_FLASH
1970be60a902SHaavard Skinnemoen 	flash_protect (FLAG_PROTECT_SET,
1971be60a902SHaavard Skinnemoen 		       CFG_ENV_ADDR,
1972be60a902SHaavard Skinnemoen 		       CFG_ENV_ADDR + CFG_ENV_SECT_SIZE - 1,
1973be60a902SHaavard Skinnemoen 		       flash_get_info(CFG_ENV_ADDR));
1974be60a902SHaavard Skinnemoen #endif
1975be60a902SHaavard Skinnemoen 
1976be60a902SHaavard Skinnemoen 	/* Redundant environment protection ON by default */
1977be60a902SHaavard Skinnemoen #ifdef CFG_ENV_ADDR_REDUND
1978be60a902SHaavard Skinnemoen 	flash_protect (FLAG_PROTECT_SET,
1979be60a902SHaavard Skinnemoen 		       CFG_ENV_ADDR_REDUND,
1980be60a902SHaavard Skinnemoen 		       CFG_ENV_ADDR_REDUND + CFG_ENV_SIZE_REDUND - 1,
1981be60a902SHaavard Skinnemoen 		       flash_get_info(CFG_ENV_ADDR_REDUND));
1982be60a902SHaavard Skinnemoen #endif
1983be60a902SHaavard Skinnemoen 	return (size);
198459829cc1SJean-Christophe PLAGNIOL-VILLARD }
198559829cc1SJean-Christophe PLAGNIOL-VILLARD 
198659829cc1SJean-Christophe PLAGNIOL-VILLARD #endif /* CFG_FLASH_CFI */
1987