blob: 6eea49a11ea752c1b77ccade8447ea8b9a601984 [file] [log] [blame]
wdenk2cefd152004-02-08 22:55:38 +00001/*
wdenke65527f2004-02-12 00:47:09 +00002 * (C) Copyright 2002-2004
wdenk2cefd152004-02-08 22:55:38 +00003 * Brad Kemp, Seranoa Networks, Brad.Kemp@seranoa.com
4 *
5 * Copyright (C) 2003 Arabella Software Ltd.
6 * Yuli Barcohen <yuli@arabellasw.com>
wdenk2cefd152004-02-08 22:55:38 +00007 *
wdenke65527f2004-02-12 00:47:09 +00008 * Copyright (C) 2004
9 * Ed Okerson
Stefan Roese12797482006-11-13 13:55:24 +010010 *
11 * Copyright (C) 2006
12 * Tolunay Orkun <listmember@orkun.us>
wdenke65527f2004-02-12 00:47:09 +000013 *
wdenk2cefd152004-02-08 22:55:38 +000014 * See file CREDITS for list of people who contributed to this
15 * project.
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License as
19 * published by the Free Software Foundation; either version 2 of
20 * the License, or (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
30 * MA 02111-1307 USA
31 *
wdenk2cefd152004-02-08 22:55:38 +000032 */
33
34/* The DEBUG define must be before common to enable debugging */
wdenk2ebee312004-02-23 19:30:57 +000035/* #define DEBUG */
36
wdenk2cefd152004-02-08 22:55:38 +000037#include <common.h>
38#include <asm/processor.h>
Haiying Wangc123a382007-02-21 16:52:31 +010039#include <asm/io.h>
wdenkaeba06f2004-06-09 17:34:58 +000040#include <asm/byteorder.h>
wdenkd0245fc2005-04-13 10:02:42 +000041#include <environment.h>
wdenke537b3b2004-02-23 23:54:43 +000042
wdenk2cefd152004-02-08 22:55:38 +000043/*
Haavard Skinnemoend523e392007-12-13 12:56:28 +010044 * This file implements a Common Flash Interface (CFI) driver for
45 * U-Boot.
46 *
47 * The width of the port and the width of the chips are determined at
48 * initialization. These widths are used to calculate the address for
49 * access CFI data structures.
wdenk2cefd152004-02-08 22:55:38 +000050 *
51 * References
52 * JEDEC Standard JESD68 - Common Flash Interface (CFI)
53 * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes
54 * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets
55 * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet
Stefan Roese12797482006-11-13 13:55:24 +010056 * AMD CFI Specification, Release 2.0 December 1, 2001
57 * AMD/Spansion Application Note: Migration from Single-byte to Three-byte
58 * Device IDs, Publication Number 25538 Revision A, November 8, 2001
wdenk2cefd152004-02-08 22:55:38 +000059 *
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020060 * Define CONFIG_SYS_WRITE_SWAPPED_DATA, if you have to swap the Bytes between
Heiko Schocher800db312007-01-19 18:05:26 +010061 * reading and writing ... (yes there is such a Hardware).
wdenk2cefd152004-02-08 22:55:38 +000062 */
63
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020064#ifndef CONFIG_SYS_FLASH_BANKS_LIST
65#define CONFIG_SYS_FLASH_BANKS_LIST { CONFIG_SYS_FLASH_BASE }
wdenke65527f2004-02-12 00:47:09 +000066#endif
67
wdenk2cefd152004-02-08 22:55:38 +000068#define FLASH_CMD_CFI 0x98
69#define FLASH_CMD_READ_ID 0x90
70#define FLASH_CMD_RESET 0xff
71#define FLASH_CMD_BLOCK_ERASE 0x20
72#define FLASH_CMD_ERASE_CONFIRM 0xD0
73#define FLASH_CMD_WRITE 0x40
74#define FLASH_CMD_PROTECT 0x60
75#define FLASH_CMD_PROTECT_SET 0x01
76#define FLASH_CMD_PROTECT_CLEAR 0xD0
77#define FLASH_CMD_CLEAR_STATUS 0x50
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +040078#define FLASH_CMD_READ_STATUS 0x70
wdenke65527f2004-02-12 00:47:09 +000079#define FLASH_CMD_WRITE_TO_BUFFER 0xE8
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +040080#define FLASH_CMD_WRITE_BUFFER_PROG 0xE9
wdenke65527f2004-02-12 00:47:09 +000081#define FLASH_CMD_WRITE_BUFFER_CONFIRM 0xD0
wdenk2cefd152004-02-08 22:55:38 +000082
83#define FLASH_STATUS_DONE 0x80
84#define FLASH_STATUS_ESS 0x40
85#define FLASH_STATUS_ECLBS 0x20
86#define FLASH_STATUS_PSLBS 0x10
87#define FLASH_STATUS_VPENS 0x08
88#define FLASH_STATUS_PSS 0x04
89#define FLASH_STATUS_DPS 0x02
90#define FLASH_STATUS_R 0x01
91#define FLASH_STATUS_PROTECT 0x01
92
93#define AMD_CMD_RESET 0xF0
94#define AMD_CMD_WRITE 0xA0
95#define AMD_CMD_ERASE_START 0x80
96#define AMD_CMD_ERASE_SECTOR 0x30
wdenked2ac4b2004-03-14 18:23:55 +000097#define AMD_CMD_UNLOCK_START 0xAA
98#define AMD_CMD_UNLOCK_ACK 0x55
Stefan Roesec865e6c2006-02-28 15:29:58 +010099#define AMD_CMD_WRITE_TO_BUFFER 0x25
100#define AMD_CMD_WRITE_BUFFER_CONFIRM 0x29
wdenk2cefd152004-02-08 22:55:38 +0000101
102#define AMD_STATUS_TOGGLE 0x40
103#define AMD_STATUS_ERROR 0x20
Stefan Roesec865e6c2006-02-28 15:29:58 +0100104
Rafael Campos13d2b612008-07-31 10:22:20 +0200105#define ATM_CMD_UNLOCK_SECT 0x70
106#define ATM_CMD_SOFTLOCK_START 0x80
107#define ATM_CMD_LOCK_SECT 0x40
108
Niklaus Gigerf447f712009-07-22 17:13:24 +0200109#define FLASH_CONTINUATION_CODE 0x7F
110
Stefan Roese12797482006-11-13 13:55:24 +0100111#define FLASH_OFFSET_MANUFACTURER_ID 0x00
112#define FLASH_OFFSET_DEVICE_ID 0x01
113#define FLASH_OFFSET_DEVICE_ID2 0x0E
114#define FLASH_OFFSET_DEVICE_ID3 0x0F
wdenk2cefd152004-02-08 22:55:38 +0000115#define FLASH_OFFSET_CFI 0x55
Wolfgang Denkafa0dd02006-12-27 01:26:13 +0100116#define FLASH_OFFSET_CFI_ALT 0x555
wdenk2cefd152004-02-08 22:55:38 +0000117#define FLASH_OFFSET_CFI_RESP 0x10
wdenke65527f2004-02-12 00:47:09 +0000118#define FLASH_OFFSET_PRIMARY_VENDOR 0x13
Haavard Skinnemoend523e392007-12-13 12:56:28 +0100119/* extended query table primary address */
120#define FLASH_OFFSET_EXT_QUERY_T_P_ADDR 0x15
wdenk2cefd152004-02-08 22:55:38 +0000121#define FLASH_OFFSET_WTOUT 0x1F
wdenke65527f2004-02-12 00:47:09 +0000122#define FLASH_OFFSET_WBTOUT 0x20
wdenk2cefd152004-02-08 22:55:38 +0000123#define FLASH_OFFSET_ETOUT 0x21
wdenke65527f2004-02-12 00:47:09 +0000124#define FLASH_OFFSET_CETOUT 0x22
wdenk2cefd152004-02-08 22:55:38 +0000125#define FLASH_OFFSET_WMAX_TOUT 0x23
wdenke65527f2004-02-12 00:47:09 +0000126#define FLASH_OFFSET_WBMAX_TOUT 0x24
wdenk2cefd152004-02-08 22:55:38 +0000127#define FLASH_OFFSET_EMAX_TOUT 0x25
wdenke65527f2004-02-12 00:47:09 +0000128#define FLASH_OFFSET_CEMAX_TOUT 0x26
wdenk2cefd152004-02-08 22:55:38 +0000129#define FLASH_OFFSET_SIZE 0x27
wdenke65527f2004-02-12 00:47:09 +0000130#define FLASH_OFFSET_INTERFACE 0x28
131#define FLASH_OFFSET_BUFFER_SIZE 0x2A
wdenk2cefd152004-02-08 22:55:38 +0000132#define FLASH_OFFSET_NUM_ERASE_REGIONS 0x2C
133#define FLASH_OFFSET_ERASE_REGIONS 0x2D
134#define FLASH_OFFSET_PROTECT 0x02
wdenke65527f2004-02-12 00:47:09 +0000135#define FLASH_OFFSET_USER_PROTECTION 0x85
136#define FLASH_OFFSET_INTEL_PROTECTION 0x81
wdenk2cefd152004-02-08 22:55:38 +0000137
Stefan Roese12797482006-11-13 13:55:24 +0100138#define CFI_CMDSET_NONE 0
139#define CFI_CMDSET_INTEL_EXTENDED 1
140#define CFI_CMDSET_AMD_STANDARD 2
141#define CFI_CMDSET_INTEL_STANDARD 3
142#define CFI_CMDSET_AMD_EXTENDED 4
143#define CFI_CMDSET_MITSU_STANDARD 256
144#define CFI_CMDSET_MITSU_EXTENDED 257
145#define CFI_CMDSET_SST 258
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400146#define CFI_CMDSET_INTEL_PROG_REGIONS 512
wdenk2cefd152004-02-08 22:55:38 +0000147
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200148#ifdef CONFIG_SYS_FLASH_CFI_AMD_RESET /* needed for STM_ID_29W320DB on UC100 */
wdenk51242782004-12-18 22:35:43 +0000149# undef FLASH_CMD_RESET
Stefan Roese12797482006-11-13 13:55:24 +0100150# define FLASH_CMD_RESET AMD_CMD_RESET /* use AMD-Reset instead */
wdenk51242782004-12-18 22:35:43 +0000151#endif
152
wdenk2cefd152004-02-08 22:55:38 +0000153typedef union {
154 unsigned char c;
155 unsigned short w;
156 unsigned long l;
157 unsigned long long ll;
158} cfiword_t;
159
Stefan Roese12797482006-11-13 13:55:24 +0100160#define NUM_ERASE_REGIONS 4 /* max. number of erase regions */
wdenk2cefd152004-02-08 22:55:38 +0000161
Haavard Skinnemoend523e392007-12-13 12:56:28 +0100162static uint flash_offset_cfi[2] = { FLASH_OFFSET_CFI, FLASH_OFFSET_CFI_ALT };
Piotr Ziecik2a7493c2008-11-17 15:49:32 +0100163static uint flash_verbose = 1;
Wolfgang Denkafa0dd02006-12-27 01:26:13 +0100164
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200165/* use CONFIG_SYS_MAX_FLASH_BANKS_DETECT if defined */
166#ifdef CONFIG_SYS_MAX_FLASH_BANKS_DETECT
167# define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS_DETECT
Marian Balakowicz513b4a12005-10-11 19:09:42 +0200168#else
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200169# define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS
Marian Balakowicz513b4a12005-10-11 19:09:42 +0200170#endif
wdenk2cefd152004-02-08 22:55:38 +0000171
Wolfgang Denk9f5fb0f2008-08-08 16:39:54 +0200172flash_info_t flash_info[CFI_MAX_FLASH_BANKS]; /* FLASH chips info */
173
Stefan Roesec865e6c2006-02-28 15:29:58 +0100174/*
175 * Check if chip width is defined. If not, start detecting with 8bit.
176 */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200177#ifndef CONFIG_SYS_FLASH_CFI_WIDTH
178#define CONFIG_SYS_FLASH_CFI_WIDTH FLASH_CFI_8BIT
Stefan Roesec865e6c2006-02-28 15:29:58 +0100179#endif
180
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100181/* CFI standard query structure */
182struct cfi_qry {
183 u8 qry[3];
184 u16 p_id;
185 u16 p_adr;
186 u16 a_id;
187 u16 a_adr;
188 u8 vcc_min;
189 u8 vcc_max;
190 u8 vpp_min;
191 u8 vpp_max;
192 u8 word_write_timeout_typ;
193 u8 buf_write_timeout_typ;
194 u8 block_erase_timeout_typ;
195 u8 chip_erase_timeout_typ;
196 u8 word_write_timeout_max;
197 u8 buf_write_timeout_max;
198 u8 block_erase_timeout_max;
199 u8 chip_erase_timeout_max;
200 u8 dev_size;
201 u16 interface_desc;
202 u16 max_buf_write_size;
203 u8 num_erase_regions;
204 u32 erase_region_info[NUM_ERASE_REGIONS];
205} __attribute__((packed));
206
207struct cfi_pri_hdr {
208 u8 pri[3];
209 u8 major_version;
210 u8 minor_version;
211} __attribute__((packed));
212
Stefan Roese38ae9822008-11-17 14:45:22 +0100213static void __flash_write8(u8 value, void *addr)
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100214{
215 __raw_writeb(value, addr);
216}
217
Stefan Roese38ae9822008-11-17 14:45:22 +0100218static void __flash_write16(u16 value, void *addr)
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100219{
220 __raw_writew(value, addr);
221}
222
Stefan Roese38ae9822008-11-17 14:45:22 +0100223static void __flash_write32(u32 value, void *addr)
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100224{
225 __raw_writel(value, addr);
226}
227
Stefan Roese38ae9822008-11-17 14:45:22 +0100228static void __flash_write64(u64 value, void *addr)
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100229{
230 /* No architectures currently implement __raw_writeq() */
231 *(volatile u64 *)addr = value;
232}
233
Stefan Roese38ae9822008-11-17 14:45:22 +0100234static u8 __flash_read8(void *addr)
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100235{
236 return __raw_readb(addr);
237}
238
Stefan Roese38ae9822008-11-17 14:45:22 +0100239static u16 __flash_read16(void *addr)
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100240{
241 return __raw_readw(addr);
242}
243
Stefan Roese38ae9822008-11-17 14:45:22 +0100244static u32 __flash_read32(void *addr)
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100245{
246 return __raw_readl(addr);
247}
248
Daniel Hellstromcfd71382008-03-28 20:40:19 +0100249static u64 __flash_read64(void *addr)
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100250{
251 /* No architectures currently implement __raw_readq() */
252 return *(volatile u64 *)addr;
253}
254
Stefan Roese38ae9822008-11-17 14:45:22 +0100255#ifdef CONFIG_CFI_FLASH_USE_WEAK_ACCESSORS
256void flash_write8(u8 value, void *addr)__attribute__((weak, alias("__flash_write8")));
257void flash_write16(u16 value, void *addr)__attribute__((weak, alias("__flash_write16")));
258void flash_write32(u32 value, void *addr)__attribute__((weak, alias("__flash_write32")));
259void flash_write64(u64 value, void *addr)__attribute__((weak, alias("__flash_write64")));
260u8 flash_read8(void *addr)__attribute__((weak, alias("__flash_read8")));
261u16 flash_read16(void *addr)__attribute__((weak, alias("__flash_read16")));
262u32 flash_read32(void *addr)__attribute__((weak, alias("__flash_read32")));
Daniel Hellstromcfd71382008-03-28 20:40:19 +0100263u64 flash_read64(void *addr)__attribute__((weak, alias("__flash_read64")));
Stefan Roese38ae9822008-11-17 14:45:22 +0100264#else
265#define flash_write8 __flash_write8
266#define flash_write16 __flash_write16
267#define flash_write32 __flash_write32
268#define flash_write64 __flash_write64
269#define flash_read8 __flash_read8
270#define flash_read16 __flash_read16
271#define flash_read32 __flash_read32
272#define flash_read64 __flash_read64
273#endif
Daniel Hellstromcfd71382008-03-28 20:40:19 +0100274
wdenk2cefd152004-02-08 22:55:38 +0000275/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +0000276 */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200277#if defined(CONFIG_ENV_IS_IN_FLASH) || defined(CONFIG_ENV_ADDR_REDUND) || (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE)
Heiko Schocher4c0f0052009-02-10 09:53:29 +0100278flash_info_t *flash_get_info(ulong base)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200279{
280 int i;
281 flash_info_t * info = 0;
wdenk2cefd152004-02-08 22:55:38 +0000282
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200283 for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200284 info = & flash_info[i];
285 if (info->size && info->start[0] <= base &&
286 base <= info->start[0] + info->size - 1)
287 break;
288 }
wdenk2cefd152004-02-08 22:55:38 +0000289
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200290 return i == CONFIG_SYS_MAX_FLASH_BANKS ? 0 : info;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200291}
wdenk2cefd152004-02-08 22:55:38 +0000292#endif
293
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100294unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect)
295{
296 if (sect != (info->sector_count - 1))
297 return info->start[sect + 1] - info->start[sect];
298 else
299 return info->start[0] + info->size - info->start[sect];
300}
301
wdenke65527f2004-02-12 00:47:09 +0000302/*-----------------------------------------------------------------------
303 * create an address based on the offset and the port width
304 */
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100305static inline void *
306flash_map (flash_info_t * info, flash_sect_t sect, uint offset)
wdenke65527f2004-02-12 00:47:09 +0000307{
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100308 unsigned int byte_offset = offset * info->portwidth;
309
Becky Bruce9d1f6af2009-02-02 16:34:51 -0600310 return (void *)(info->start[sect] + byte_offset);
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100311}
312
313static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
314 unsigned int offset, void *addr)
315{
wdenke65527f2004-02-12 00:47:09 +0000316}
317
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200318/*-----------------------------------------------------------------------
319 * make a proper sized command based on the port and chip widths
320 */
Sebastian Siewior7746ed82008-07-15 13:35:23 +0200321static void flash_make_cmd(flash_info_t *info, u32 cmd, void *cmdbuf)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200322{
323 int i;
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400324 int cword_offset;
325 int cp_offset;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200326#if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA)
Sebastian Siewiord528cd62008-07-16 20:04:49 +0200327 u32 cmd_le = cpu_to_le32(cmd);
328#endif
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400329 uchar val;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200330 uchar *cp = (uchar *) cmdbuf;
331
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400332 for (i = info->portwidth; i > 0; i--){
333 cword_offset = (info->portwidth-i)%info->chipwidth;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200334#if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA)
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400335 cp_offset = info->portwidth - i;
Sebastian Siewiord528cd62008-07-16 20:04:49 +0200336 val = *((uchar*)&cmd_le + cword_offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200337#else
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400338 cp_offset = i - 1;
Sebastian Siewior7746ed82008-07-15 13:35:23 +0200339 val = *((uchar*)&cmd + sizeof(u32) - cword_offset - 1);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200340#endif
Sebastian Siewior7746ed82008-07-15 13:35:23 +0200341 cp[cp_offset] = (cword_offset >= sizeof(u32)) ? 0x00 : val;
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400342 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200343}
344
wdenk2cefd152004-02-08 22:55:38 +0000345#ifdef DEBUG
wdenke65527f2004-02-12 00:47:09 +0000346/*-----------------------------------------------------------------------
347 * Debug support
348 */
Haavard Skinnemoen670a3232007-12-13 12:56:29 +0100349static void print_longlong (char *str, unsigned long long data)
wdenk2cefd152004-02-08 22:55:38 +0000350{
351 int i;
352 char *cp;
wdenke65527f2004-02-12 00:47:09 +0000353
Wolfgang Denk49f4f6a2009-02-04 09:42:20 +0100354 cp = (char *) &data;
wdenke65527f2004-02-12 00:47:09 +0000355 for (i = 0; i < 8; i++)
356 sprintf (&str[i * 2], "%2.2x", *cp++);
wdenk2cefd152004-02-08 22:55:38 +0000357}
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200358
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100359static void flash_printqry (struct cfi_qry *qry)
wdenke65527f2004-02-12 00:47:09 +0000360{
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100361 u8 *p = (u8 *)qry;
wdenke65527f2004-02-12 00:47:09 +0000362 int x, y;
363
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100364 for (x = 0; x < sizeof(struct cfi_qry); x += 16) {
365 debug("%02x : ", x);
366 for (y = 0; y < 16; y++)
367 debug("%2.2x ", p[x + y]);
368 debug(" ");
wdenke65527f2004-02-12 00:47:09 +0000369 for (y = 0; y < 16; y++) {
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100370 unsigned char c = p[x + y];
371 if (c >= 0x20 && c <= 0x7e)
372 debug("%c", c);
373 else
374 debug(".");
wdenke65527f2004-02-12 00:47:09 +0000375 }
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100376 debug("\n");
wdenke65527f2004-02-12 00:47:09 +0000377 }
378}
wdenk2cefd152004-02-08 22:55:38 +0000379#endif
380
381
382/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +0000383 * read a character at a port width address
384 */
Haavard Skinnemoen670a3232007-12-13 12:56:29 +0100385static inline uchar flash_read_uchar (flash_info_t * info, uint offset)
wdenk2cefd152004-02-08 22:55:38 +0000386{
387 uchar *cp;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100388 uchar retval;
wdenke65527f2004-02-12 00:47:09 +0000389
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100390 cp = flash_map (info, 0, offset);
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200391#if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA)
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100392 retval = flash_read8(cp);
wdenke65527f2004-02-12 00:47:09 +0000393#else
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100394 retval = flash_read8(cp + info->portwidth - 1);
wdenke65527f2004-02-12 00:47:09 +0000395#endif
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100396 flash_unmap (info, 0, offset, cp);
397 return retval;
wdenk2cefd152004-02-08 22:55:38 +0000398}
399
400/*-----------------------------------------------------------------------
Tor Krill7f2a3052008-03-28 11:29:10 +0100401 * read a word at a port width address, assume 16bit bus
402 */
403static inline ushort flash_read_word (flash_info_t * info, uint offset)
404{
405 ushort *addr, retval;
406
407 addr = flash_map (info, 0, offset);
408 retval = flash_read16 (addr);
409 flash_unmap (info, 0, offset, addr);
410 return retval;
411}
412
413
414/*-----------------------------------------------------------------------
Stefan Roese12797482006-11-13 13:55:24 +0100415 * read a long word by picking the least significant byte of each maximum
wdenk2cefd152004-02-08 22:55:38 +0000416 * port size word. Swap for ppc format.
417 */
Haavard Skinnemoen670a3232007-12-13 12:56:29 +0100418static ulong flash_read_long (flash_info_t * info, flash_sect_t sect,
419 uint offset)
wdenk2cefd152004-02-08 22:55:38 +0000420{
wdenke65527f2004-02-12 00:47:09 +0000421 uchar *addr;
422 ulong retval;
423
424#ifdef DEBUG
425 int x;
426#endif
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100427 addr = flash_map (info, sect, offset);
wdenk2cefd152004-02-08 22:55:38 +0000428
wdenke65527f2004-02-12 00:47:09 +0000429#ifdef DEBUG
430 debug ("long addr is at %p info->portwidth = %d\n", addr,
431 info->portwidth);
432 for (x = 0; x < 4 * info->portwidth; x++) {
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100433 debug ("addr[%x] = 0x%x\n", x, flash_read8(addr + x));
wdenke65527f2004-02-12 00:47:09 +0000434 }
435#endif
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200436#if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA)
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100437 retval = ((flash_read8(addr) << 16) |
438 (flash_read8(addr + info->portwidth) << 24) |
439 (flash_read8(addr + 2 * info->portwidth)) |
440 (flash_read8(addr + 3 * info->portwidth) << 8));
wdenke65527f2004-02-12 00:47:09 +0000441#else
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100442 retval = ((flash_read8(addr + 2 * info->portwidth - 1) << 24) |
443 (flash_read8(addr + info->portwidth - 1) << 16) |
444 (flash_read8(addr + 4 * info->portwidth - 1) << 8) |
445 (flash_read8(addr + 3 * info->portwidth - 1)));
wdenke65527f2004-02-12 00:47:09 +0000446#endif
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100447 flash_unmap(info, sect, offset, addr);
448
wdenke65527f2004-02-12 00:47:09 +0000449 return retval;
wdenk2cefd152004-02-08 22:55:38 +0000450}
451
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200452/*
453 * Write a proper sized command to the correct address
Michael Schwingen73d044d2007-12-07 23:35:02 +0100454 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200455static void flash_write_cmd (flash_info_t * info, flash_sect_t sect,
Sebastian Siewior7746ed82008-07-15 13:35:23 +0200456 uint offset, u32 cmd)
Michael Schwingen73d044d2007-12-07 23:35:02 +0100457{
Haavard Skinnemoend523e392007-12-13 12:56:28 +0100458
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100459 void *addr;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200460 cfiword_t cword;
Michael Schwingen73d044d2007-12-07 23:35:02 +0100461
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100462 addr = flash_map (info, sect, offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200463 flash_make_cmd (info, cmd, &cword);
464 switch (info->portwidth) {
465 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100466 debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr, cmd,
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200467 cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100468 flash_write8(cword.c, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200469 break;
470 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100471 debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr,
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200472 cmd, cword.w,
473 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100474 flash_write16(cword.w, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200475 break;
476 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100477 debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit\n", addr,
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200478 cmd, cword.l,
479 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100480 flash_write32(cword.l, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200481 break;
482 case FLASH_CFI_64BIT:
483#ifdef DEBUG
484 {
485 char str[20];
Haavard Skinnemoend523e392007-12-13 12:56:28 +0100486
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200487 print_longlong (str, cword.ll);
488
489 debug ("fwrite addr %p cmd %x %s 64 bit x %d bit\n",
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100490 addr, cmd, str,
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200491 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
Michael Schwingen73d044d2007-12-07 23:35:02 +0100492 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200493#endif
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100494 flash_write64(cword.ll, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200495 break;
Michael Schwingen73d044d2007-12-07 23:35:02 +0100496 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200497
498 /* Ensure all the instructions are fully finished */
499 sync();
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100500
501 flash_unmap(info, sect, offset, addr);
Michael Schwingen73d044d2007-12-07 23:35:02 +0100502}
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200503
504static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect)
Michael Schwingen73d044d2007-12-07 23:35:02 +0100505{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200506 flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_UNLOCK_START);
507 flash_write_cmd (info, sect, info->addr_unlock2, AMD_CMD_UNLOCK_ACK);
Michael Schwingen73d044d2007-12-07 23:35:02 +0100508}
Michael Schwingen73d044d2007-12-07 23:35:02 +0100509
510/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +0000511 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200512static int flash_isequal (flash_info_t * info, flash_sect_t sect,
513 uint offset, uchar cmd)
wdenk2cefd152004-02-08 22:55:38 +0000514{
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100515 void *addr;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200516 cfiword_t cword;
517 int retval;
wdenk2cefd152004-02-08 22:55:38 +0000518
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100519 addr = flash_map (info, sect, offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200520 flash_make_cmd (info, cmd, &cword);
Stefan Roeseefef95b2006-04-01 13:41:03 +0200521
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100522 debug ("is= cmd %x(%c) addr %p ", cmd, cmd, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200523 switch (info->portwidth) {
524 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100525 debug ("is= %x %x\n", flash_read8(addr), cword.c);
526 retval = (flash_read8(addr) == cword.c);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200527 break;
528 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100529 debug ("is= %4.4x %4.4x\n", flash_read16(addr), cword.w);
530 retval = (flash_read16(addr) == cword.w);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200531 break;
532 case FLASH_CFI_32BIT:
Andrew Klossner7ddfafc2008-08-21 07:12:26 -0700533 debug ("is= %8.8x %8.8lx\n", flash_read32(addr), cword.l);
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100534 retval = (flash_read32(addr) == cword.l);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200535 break;
536 case FLASH_CFI_64BIT:
537#ifdef DEBUG
538 {
539 char str1[20];
540 char str2[20];
Michael Schwingen73d044d2007-12-07 23:35:02 +0100541
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100542 print_longlong (str1, flash_read64(addr));
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200543 print_longlong (str2, cword.ll);
544 debug ("is= %s %s\n", str1, str2);
wdenk2cefd152004-02-08 22:55:38 +0000545 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200546#endif
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100547 retval = (flash_read64(addr) == cword.ll);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200548 break;
549 default:
550 retval = 0;
551 break;
552 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100553 flash_unmap(info, sect, offset, addr);
554
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200555 return retval;
556}
Stefan Roeseefef95b2006-04-01 13:41:03 +0200557
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200558/*-----------------------------------------------------------------------
559 */
560static int flash_isset (flash_info_t * info, flash_sect_t sect,
561 uint offset, uchar cmd)
562{
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100563 void *addr;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200564 cfiword_t cword;
565 int retval;
Stefan Roeseefef95b2006-04-01 13:41:03 +0200566
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100567 addr = flash_map (info, sect, offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200568 flash_make_cmd (info, cmd, &cword);
569 switch (info->portwidth) {
570 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100571 retval = ((flash_read8(addr) & cword.c) == cword.c);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200572 break;
573 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100574 retval = ((flash_read16(addr) & cword.w) == cword.w);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200575 break;
576 case FLASH_CFI_32BIT:
Stefan Roesed4e37c02008-01-02 14:05:37 +0100577 retval = ((flash_read32(addr) & cword.l) == cword.l);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200578 break;
579 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100580 retval = ((flash_read64(addr) & cword.ll) == cword.ll);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200581 break;
582 default:
583 retval = 0;
584 break;
585 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100586 flash_unmap(info, sect, offset, addr);
587
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200588 return retval;
589}
Stefan Roeseefef95b2006-04-01 13:41:03 +0200590
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200591/*-----------------------------------------------------------------------
592 */
593static int flash_toggle (flash_info_t * info, flash_sect_t sect,
594 uint offset, uchar cmd)
595{
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100596 void *addr;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200597 cfiword_t cword;
598 int retval;
wdenke85b7a52004-10-10 22:16:06 +0000599
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100600 addr = flash_map (info, sect, offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200601 flash_make_cmd (info, cmd, &cword);
602 switch (info->portwidth) {
603 case FLASH_CFI_8BIT:
Stefan Roesecff2b492008-06-16 10:40:02 +0200604 retval = flash_read8(addr) != flash_read8(addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200605 break;
606 case FLASH_CFI_16BIT:
Stefan Roesecff2b492008-06-16 10:40:02 +0200607 retval = flash_read16(addr) != flash_read16(addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200608 break;
609 case FLASH_CFI_32BIT:
Stefan Roesecff2b492008-06-16 10:40:02 +0200610 retval = flash_read32(addr) != flash_read32(addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200611 break;
612 case FLASH_CFI_64BIT:
Wolfgang Denk600e1832008-10-31 01:12:28 +0100613 retval = ( (flash_read32( addr ) != flash_read32( addr )) ||
614 (flash_read32(addr+4) != flash_read32(addr+4)) );
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200615 break;
616 default:
617 retval = 0;
618 break;
619 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100620 flash_unmap(info, sect, offset, addr);
621
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200622 return retval;
wdenk2cefd152004-02-08 22:55:38 +0000623}
624
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200625/*
626 * flash_is_busy - check to see if the flash is busy
627 *
628 * This routine checks the status of the chip and returns true if the
629 * chip is busy.
wdenk2cefd152004-02-08 22:55:38 +0000630 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200631static int flash_is_busy (flash_info_t * info, flash_sect_t sect)
wdenk5c71a7a2005-05-16 15:23:22 +0000632{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200633 int retval;
wdenk5c71a7a2005-05-16 15:23:22 +0000634
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200635 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400636 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200637 case CFI_CMDSET_INTEL_STANDARD:
638 case CFI_CMDSET_INTEL_EXTENDED:
639 retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE);
640 break;
641 case CFI_CMDSET_AMD_STANDARD:
642 case CFI_CMDSET_AMD_EXTENDED:
643#ifdef CONFIG_FLASH_CFI_LEGACY
644 case CFI_CMDSET_AMD_LEGACY:
645#endif
646 retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE);
647 break;
648 default:
649 retval = 0;
wdenk5c71a7a2005-05-16 15:23:22 +0000650 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200651 debug ("flash_is_busy: %d\n", retval);
652 return retval;
wdenk5c71a7a2005-05-16 15:23:22 +0000653}
654
655/*-----------------------------------------------------------------------
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200656 * wait for XSR.7 to be set. Time out with an error if it does not.
657 * This routine does not set the flash to read-array mode.
wdenk5c71a7a2005-05-16 15:23:22 +0000658 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200659static int flash_status_check (flash_info_t * info, flash_sect_t sector,
660 ulong tout, char *prompt)
wdenk2cefd152004-02-08 22:55:38 +0000661{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200662 ulong start;
wdenk2cefd152004-02-08 22:55:38 +0000663
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200664#if CONFIG_SYS_HZ != 1000
665 tout *= CONFIG_SYS_HZ/1000;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200666#endif
wdenk2cefd152004-02-08 22:55:38 +0000667
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200668 /* Wait for command completion */
669 start = get_timer (0);
670 while (flash_is_busy (info, sector)) {
671 if (get_timer (start) > tout) {
672 printf ("Flash %s timeout at address %lx data %lx\n",
673 prompt, info->start[sector],
674 flash_read_long (info, sector, 0));
675 flash_write_cmd (info, sector, 0, info->cmd_reset);
676 return ERR_TIMOUT;
wdenk2cefd152004-02-08 22:55:38 +0000677 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200678 udelay (1); /* also triggers watchdog */
wdenk2cefd152004-02-08 22:55:38 +0000679 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200680 return ERR_OK;
681}
wdenk2cefd152004-02-08 22:55:38 +0000682
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200683/*-----------------------------------------------------------------------
684 * Wait for XSR.7 to be set, if it times out print an error, otherwise
685 * do a full status check.
686 *
687 * This routine sets the flash to read-array mode.
688 */
689static int flash_full_status_check (flash_info_t * info, flash_sect_t sector,
690 ulong tout, char *prompt)
691{
692 int retcode;
wdenk2cefd152004-02-08 22:55:38 +0000693
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200694 retcode = flash_status_check (info, sector, tout, prompt);
695 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400696 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200697 case CFI_CMDSET_INTEL_EXTENDED:
698 case CFI_CMDSET_INTEL_STANDARD:
Ed Swarthout2da14102008-10-09 01:26:36 -0500699 if ((retcode != ERR_OK)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200700 && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) {
701 retcode = ERR_INVAL;
702 printf ("Flash %s error at address %lx\n", prompt,
703 info->start[sector]);
704 if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS |
705 FLASH_STATUS_PSLBS)) {
706 puts ("Command Sequence Error.\n");
707 } else if (flash_isset (info, sector, 0,
708 FLASH_STATUS_ECLBS)) {
709 puts ("Block Erase Error.\n");
710 retcode = ERR_NOT_ERASED;
711 } else if (flash_isset (info, sector, 0,
712 FLASH_STATUS_PSLBS)) {
713 puts ("Locking Error\n");
wdenk2cefd152004-02-08 22:55:38 +0000714 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200715 if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) {
716 puts ("Block locked.\n");
717 retcode = ERR_PROTECTED;
718 }
719 if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS))
720 puts ("Vpp Low Error.\n");
wdenk2cefd152004-02-08 22:55:38 +0000721 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200722 flash_write_cmd (info, sector, 0, info->cmd_reset);
723 break;
724 default:
725 break;
wdenk2cefd152004-02-08 22:55:38 +0000726 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200727 return retcode;
wdenk2cefd152004-02-08 22:55:38 +0000728}
729
730/*-----------------------------------------------------------------------
731 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200732static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
wdenk2cefd152004-02-08 22:55:38 +0000733{
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200734#if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200735 unsigned short w;
736 unsigned int l;
737 unsigned long long ll;
738#endif
wdenk2cefd152004-02-08 22:55:38 +0000739
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200740 switch (info->portwidth) {
741 case FLASH_CFI_8BIT:
742 cword->c = c;
743 break;
744 case FLASH_CFI_16BIT:
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200745#if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200746 w = c;
747 w <<= 8;
748 cword->w = (cword->w >> 8) | w;
749#else
750 cword->w = (cword->w << 8) | c;
Michael Schwingen73d044d2007-12-07 23:35:02 +0100751#endif
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200752 break;
753 case FLASH_CFI_32BIT:
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200754#if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200755 l = c;
756 l <<= 24;
757 cword->l = (cword->l >> 8) | l;
758#else
759 cword->l = (cword->l << 8) | c;
760#endif
761 break;
762 case FLASH_CFI_64BIT:
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200763#if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200764 ll = c;
765 ll <<= 56;
766 cword->ll = (cword->ll >> 8) | ll;
767#else
768 cword->ll = (cword->ll << 8) | c;
769#endif
770 break;
Stefan Roese12797482006-11-13 13:55:24 +0100771 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200772}
wdenk2cefd152004-02-08 22:55:38 +0000773
Jens Gehrlein8ee4add2008-12-16 17:25:55 +0100774/*
775 * Loop through the sector table starting from the previously found sector.
776 * Searches forwards or backwards, dependent on the passed address.
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200777 */
778static flash_sect_t find_sector (flash_info_t * info, ulong addr)
779{
Jens Gehrlein8ee4add2008-12-16 17:25:55 +0100780 static flash_sect_t saved_sector = 0; /* previously found sector */
781 flash_sect_t sector = saved_sector;
782
783 while ((info->start[sector] < addr)
784 && (sector < info->sector_count - 1))
785 sector++;
786 while ((info->start[sector] > addr) && (sector > 0))
787 /*
788 * also decrements the sector in case of an overshot
789 * in the first loop
790 */
791 sector--;
wdenk2cefd152004-02-08 22:55:38 +0000792
Jens Gehrlein8ee4add2008-12-16 17:25:55 +0100793 saved_sector = sector;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200794 return sector;
wdenk2cefd152004-02-08 22:55:38 +0000795}
796
797/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +0000798 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200799static int flash_write_cfiword (flash_info_t * info, ulong dest,
800 cfiword_t cword)
wdenk2cefd152004-02-08 22:55:38 +0000801{
Becky Bruce9d1f6af2009-02-02 16:34:51 -0600802 void *dstaddr = (void *)dest;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200803 int flag;
Jens Gehrlein1e814cf2008-12-16 17:25:54 +0100804 flash_sect_t sect = 0;
805 char sect_found = 0;
wdenk2cefd152004-02-08 22:55:38 +0000806
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200807 /* Check if Flash is (sufficiently) erased */
808 switch (info->portwidth) {
809 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100810 flag = ((flash_read8(dstaddr) & cword.c) == cword.c);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200811 break;
812 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100813 flag = ((flash_read16(dstaddr) & cword.w) == cword.w);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200814 break;
815 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100816 flag = ((flash_read32(dstaddr) & cword.l) == cword.l);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200817 break;
818 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100819 flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200820 break;
821 default:
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100822 flag = 0;
823 break;
wdenk2cefd152004-02-08 22:55:38 +0000824 }
Becky Bruce9d1f6af2009-02-02 16:34:51 -0600825 if (!flag)
Stefan Roese707c1462007-12-27 07:50:54 +0100826 return ERR_NOT_ERASED;
wdenk2cefd152004-02-08 22:55:38 +0000827
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200828 /* Disable interrupts which might cause a timeout here */
829 flag = disable_interrupts ();
Stefan Roesec865e6c2006-02-28 15:29:58 +0100830
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200831 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400832 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200833 case CFI_CMDSET_INTEL_EXTENDED:
834 case CFI_CMDSET_INTEL_STANDARD:
835 flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS);
836 flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE);
837 break;
838 case CFI_CMDSET_AMD_EXTENDED:
839 case CFI_CMDSET_AMD_STANDARD:
Ed Swarthout2da14102008-10-09 01:26:36 -0500840 sect = find_sector(info, dest);
841 flash_unlock_seq (info, sect);
842 flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_WRITE);
Jens Gehrlein1e814cf2008-12-16 17:25:54 +0100843 sect_found = 1;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200844 break;
Po-Yu Chuang3f483402009-07-10 18:03:57 +0800845#ifdef CONFIG_FLASH_CFI_LEGACY
846 case CFI_CMDSET_AMD_LEGACY:
847 sect = find_sector(info, dest);
848 flash_unlock_seq (info, 0);
849 flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_WRITE);
850 sect_found = 1;
851 break;
852#endif
wdenk2cefd152004-02-08 22:55:38 +0000853 }
854
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200855 switch (info->portwidth) {
856 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100857 flash_write8(cword.c, dstaddr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200858 break;
859 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100860 flash_write16(cword.w, dstaddr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200861 break;
862 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100863 flash_write32(cword.l, dstaddr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200864 break;
865 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100866 flash_write64(cword.ll, dstaddr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200867 break;
wdenk2cefd152004-02-08 22:55:38 +0000868 }
869
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200870 /* re-enable interrupts if necessary */
871 if (flag)
872 enable_interrupts ();
wdenk2cefd152004-02-08 22:55:38 +0000873
Jens Gehrlein1e814cf2008-12-16 17:25:54 +0100874 if (!sect_found)
875 sect = find_sector (info, dest);
876
877 return flash_full_status_check (info, sect, info->write_tout, "write");
wdenk2cefd152004-02-08 22:55:38 +0000878}
879
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200880#ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE
wdenk2cefd152004-02-08 22:55:38 +0000881
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200882static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp,
883 int len)
wdenk2cefd152004-02-08 22:55:38 +0000884{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200885 flash_sect_t sector;
886 int cnt;
887 int retcode;
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100888 void *src = cp;
Stefan Roese42c6ace2009-02-05 11:25:57 +0100889 void *dst = (void *)dest;
Stefan Roese707c1462007-12-27 07:50:54 +0100890 void *dst2 = dst;
891 int flag = 0;
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200892 uint offset = 0;
893 unsigned int shift;
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400894 uchar write_cmd;
Stefan Roese707c1462007-12-27 07:50:54 +0100895
896 switch (info->portwidth) {
897 case FLASH_CFI_8BIT:
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200898 shift = 0;
Stefan Roese707c1462007-12-27 07:50:54 +0100899 break;
900 case FLASH_CFI_16BIT:
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200901 shift = 1;
Stefan Roese707c1462007-12-27 07:50:54 +0100902 break;
903 case FLASH_CFI_32BIT:
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200904 shift = 2;
Stefan Roese707c1462007-12-27 07:50:54 +0100905 break;
906 case FLASH_CFI_64BIT:
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200907 shift = 3;
Stefan Roese707c1462007-12-27 07:50:54 +0100908 break;
909 default:
910 retcode = ERR_INVAL;
911 goto out_unmap;
912 }
913
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200914 cnt = len >> shift;
915
Stefan Roese707c1462007-12-27 07:50:54 +0100916 while ((cnt-- > 0) && (flag == 0)) {
917 switch (info->portwidth) {
918 case FLASH_CFI_8BIT:
919 flag = ((flash_read8(dst2) & flash_read8(src)) ==
920 flash_read8(src));
921 src += 1, dst2 += 1;
922 break;
923 case FLASH_CFI_16BIT:
924 flag = ((flash_read16(dst2) & flash_read16(src)) ==
925 flash_read16(src));
926 src += 2, dst2 += 2;
927 break;
928 case FLASH_CFI_32BIT:
929 flag = ((flash_read32(dst2) & flash_read32(src)) ==
930 flash_read32(src));
931 src += 4, dst2 += 4;
932 break;
933 case FLASH_CFI_64BIT:
934 flag = ((flash_read64(dst2) & flash_read64(src)) ==
935 flash_read64(src));
936 src += 8, dst2 += 8;
937 break;
938 }
939 }
940 if (!flag) {
941 retcode = ERR_NOT_ERASED;
942 goto out_unmap;
943 }
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100944
Stefan Roese707c1462007-12-27 07:50:54 +0100945 src = cp;
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100946 sector = find_sector (info, dest);
wdenke65527f2004-02-12 00:47:09 +0000947
948 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400949 case CFI_CMDSET_INTEL_PROG_REGIONS:
wdenk2cefd152004-02-08 22:55:38 +0000950 case CFI_CMDSET_INTEL_STANDARD:
951 case CFI_CMDSET_INTEL_EXTENDED:
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400952 write_cmd = (info->vendor == CFI_CMDSET_INTEL_PROG_REGIONS) ?
953 FLASH_CMD_WRITE_BUFFER_PROG : FLASH_CMD_WRITE_TO_BUFFER;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200954 flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS);
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400955 flash_write_cmd (info, sector, 0, FLASH_CMD_READ_STATUS);
956 flash_write_cmd (info, sector, 0, write_cmd);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200957 retcode = flash_status_check (info, sector,
958 info->buffer_write_tout,
959 "write to buffer");
960 if (retcode == ERR_OK) {
961 /* reduce the number of loops by the width of
962 * the port */
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200963 cnt = len >> shift;
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400964 flash_write_cmd (info, sector, 0, cnt - 1);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200965 while (cnt-- > 0) {
966 switch (info->portwidth) {
967 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100968 flash_write8(flash_read8(src), dst);
969 src += 1, dst += 1;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200970 break;
971 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100972 flash_write16(flash_read16(src), dst);
973 src += 2, dst += 2;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200974 break;
975 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100976 flash_write32(flash_read32(src), dst);
977 src += 4, dst += 4;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200978 break;
979 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100980 flash_write64(flash_read64(src), dst);
981 src += 8, dst += 8;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200982 break;
983 default:
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100984 retcode = ERR_INVAL;
985 goto out_unmap;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200986 }
987 }
988 flash_write_cmd (info, sector, 0,
989 FLASH_CMD_WRITE_BUFFER_CONFIRM);
990 retcode = flash_full_status_check (
991 info, sector, info->buffer_write_tout,
992 "buffer write");
993 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100994
995 break;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200996
wdenk2cefd152004-02-08 22:55:38 +0000997 case CFI_CMDSET_AMD_STANDARD:
998 case CFI_CMDSET_AMD_EXTENDED:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200999 flash_unlock_seq(info,0);
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +02001000
1001#ifdef CONFIG_FLASH_SPANSION_S29WS_N
1002 offset = ((unsigned long)dst - info->start[sector]) >> shift;
1003#endif
1004 flash_write_cmd(info, sector, offset, AMD_CMD_WRITE_TO_BUFFER);
1005 cnt = len >> shift;
John Schmolleree355882009-08-12 10:55:47 -05001006 flash_write_cmd(info, sector, offset, cnt - 1);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001007
1008 switch (info->portwidth) {
1009 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +01001010 while (cnt-- > 0) {
1011 flash_write8(flash_read8(src), dst);
1012 src += 1, dst += 1;
1013 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001014 break;
1015 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +01001016 while (cnt-- > 0) {
1017 flash_write16(flash_read16(src), dst);
1018 src += 2, dst += 2;
1019 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001020 break;
1021 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +01001022 while (cnt-- > 0) {
1023 flash_write32(flash_read32(src), dst);
1024 src += 4, dst += 4;
1025 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001026 break;
1027 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +01001028 while (cnt-- > 0) {
1029 flash_write64(flash_read64(src), dst);
1030 src += 8, dst += 8;
1031 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001032 break;
1033 default:
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001034 retcode = ERR_INVAL;
1035 goto out_unmap;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001036 }
1037
1038 flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM);
1039 retcode = flash_full_status_check (info, sector,
1040 info->buffer_write_tout,
1041 "buffer write");
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001042 break;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001043
wdenk2cefd152004-02-08 22:55:38 +00001044 default:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001045 debug ("Unknown Command Set\n");
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001046 retcode = ERR_INVAL;
1047 break;
wdenk2cefd152004-02-08 22:55:38 +00001048 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001049
1050out_unmap:
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001051 return retcode;
wdenk2cefd152004-02-08 22:55:38 +00001052}
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001053#endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001054
wdenke65527f2004-02-12 00:47:09 +00001055
wdenk2cefd152004-02-08 22:55:38 +00001056/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +00001057 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001058int flash_erase (flash_info_t * info, int s_first, int s_last)
wdenk2cefd152004-02-08 22:55:38 +00001059{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001060 int rcode = 0;
1061 int prot;
1062 flash_sect_t sect;
wdenk2cefd152004-02-08 22:55:38 +00001063
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001064 if (info->flash_id != FLASH_MAN_CFI) {
1065 puts ("Can't erase unknown flash type - aborted\n");
1066 return 1;
1067 }
1068 if ((s_first < 0) || (s_first > s_last)) {
1069 puts ("- no sectors to erase\n");
1070 return 1;
1071 }
Stefan Roeseefef95b2006-04-01 13:41:03 +02001072
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001073 prot = 0;
1074 for (sect = s_first; sect <= s_last; ++sect) {
1075 if (info->protect[sect]) {
1076 prot++;
wdenk2cefd152004-02-08 22:55:38 +00001077 }
1078 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001079 if (prot) {
1080 printf ("- Warning: %d protected sectors will not be erased!\n",
1081 prot);
Piotr Ziecik2a7493c2008-11-17 15:49:32 +01001082 } else if (flash_verbose) {
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001083 putc ('\n');
1084 }
wdenke65527f2004-02-12 00:47:09 +00001085
wdenke65527f2004-02-12 00:47:09 +00001086
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001087 for (sect = s_first; sect <= s_last; sect++) {
1088 if (info->protect[sect] == 0) { /* not protected */
1089 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001090 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001091 case CFI_CMDSET_INTEL_STANDARD:
1092 case CFI_CMDSET_INTEL_EXTENDED:
1093 flash_write_cmd (info, sect, 0,
1094 FLASH_CMD_CLEAR_STATUS);
1095 flash_write_cmd (info, sect, 0,
1096 FLASH_CMD_BLOCK_ERASE);
1097 flash_write_cmd (info, sect, 0,
1098 FLASH_CMD_ERASE_CONFIRM);
1099 break;
1100 case CFI_CMDSET_AMD_STANDARD:
1101 case CFI_CMDSET_AMD_EXTENDED:
1102 flash_unlock_seq (info, sect);
1103 flash_write_cmd (info, sect,
1104 info->addr_unlock1,
1105 AMD_CMD_ERASE_START);
1106 flash_unlock_seq (info, sect);
1107 flash_write_cmd (info, sect, 0,
1108 AMD_CMD_ERASE_SECTOR);
1109 break;
1110#ifdef CONFIG_FLASH_CFI_LEGACY
1111 case CFI_CMDSET_AMD_LEGACY:
1112 flash_unlock_seq (info, 0);
1113 flash_write_cmd (info, 0, info->addr_unlock1,
1114 AMD_CMD_ERASE_START);
1115 flash_unlock_seq (info, 0);
1116 flash_write_cmd (info, sect, 0,
1117 AMD_CMD_ERASE_SECTOR);
1118 break;
1119#endif
1120 default:
1121 debug ("Unkown flash vendor %d\n",
1122 info->vendor);
1123 break;
wdenke65527f2004-02-12 00:47:09 +00001124 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001125
1126 if (flash_full_status_check
1127 (info, sect, info->erase_blk_tout, "erase")) {
1128 rcode = 1;
Piotr Ziecik2a7493c2008-11-17 15:49:32 +01001129 } else if (flash_verbose)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001130 putc ('.');
wdenk2cefd152004-02-08 22:55:38 +00001131 }
wdenk2cefd152004-02-08 22:55:38 +00001132 }
Piotr Ziecik2a7493c2008-11-17 15:49:32 +01001133
1134 if (flash_verbose)
1135 puts (" done\n");
1136
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001137 return rcode;
wdenk2cefd152004-02-08 22:55:38 +00001138}
wdenke65527f2004-02-12 00:47:09 +00001139
wdenk2cefd152004-02-08 22:55:38 +00001140/*-----------------------------------------------------------------------
1141 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001142void flash_print_info (flash_info_t * info)
wdenk2cefd152004-02-08 22:55:38 +00001143{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001144 int i;
wdenk369d43d2004-03-14 14:09:05 +00001145
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001146 if (info->flash_id != FLASH_MAN_CFI) {
1147 puts ("missing or unknown FLASH type\n");
1148 return;
1149 }
1150
1151 printf ("%s FLASH (%d x %d)",
1152 info->name,
1153 (info->portwidth << 3), (info->chipwidth << 3));
1154 if (info->size < 1024*1024)
1155 printf (" Size: %ld kB in %d Sectors\n",
1156 info->size >> 10, info->sector_count);
1157 else
1158 printf (" Size: %ld MB in %d Sectors\n",
1159 info->size >> 20, info->sector_count);
1160 printf (" ");
1161 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001162 case CFI_CMDSET_INTEL_PROG_REGIONS:
1163 printf ("Intel Prog Regions");
1164 break;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001165 case CFI_CMDSET_INTEL_STANDARD:
1166 printf ("Intel Standard");
1167 break;
1168 case CFI_CMDSET_INTEL_EXTENDED:
1169 printf ("Intel Extended");
1170 break;
1171 case CFI_CMDSET_AMD_STANDARD:
1172 printf ("AMD Standard");
1173 break;
1174 case CFI_CMDSET_AMD_EXTENDED:
1175 printf ("AMD Extended");
1176 break;
1177#ifdef CONFIG_FLASH_CFI_LEGACY
1178 case CFI_CMDSET_AMD_LEGACY:
1179 printf ("AMD Legacy");
1180 break;
wdenk369d43d2004-03-14 14:09:05 +00001181#endif
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001182 default:
1183 printf ("Unknown (%d)", info->vendor);
1184 break;
wdenk2cefd152004-02-08 22:55:38 +00001185 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001186 printf (" command set, Manufacturer ID: 0x%02X, Device ID: 0x%02X",
1187 info->manufacturer_id, info->device_id);
1188 if (info->device_id == 0x7E) {
1189 printf("%04X", info->device_id2);
1190 }
1191 printf ("\n Erase timeout: %ld ms, write timeout: %ld ms\n",
1192 info->erase_blk_tout,
1193 info->write_tout);
1194 if (info->buffer_size > 1) {
1195 printf (" Buffer write timeout: %ld ms, "
1196 "buffer size: %d bytes\n",
1197 info->buffer_write_tout,
1198 info->buffer_size);
1199 }
wdenk2cefd152004-02-08 22:55:38 +00001200
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001201 puts ("\n Sector Start Addresses:");
1202 for (i = 0; i < info->sector_count; ++i) {
1203 if ((i % 5) == 0)
1204 printf ("\n");
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001205#ifdef CONFIG_SYS_FLASH_EMPTY_INFO
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001206 int k;
1207 int size;
1208 int erased;
1209 volatile unsigned long *flash;
wdenk2cefd152004-02-08 22:55:38 +00001210
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001211 /*
1212 * Check if whole sector is erased
1213 */
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001214 size = flash_sector_size(info, i);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001215 erased = 1;
1216 flash = (volatile unsigned long *) info->start[i];
1217 size = size >> 2; /* divide by 4 for longword access */
1218 for (k = 0; k < size; k++) {
1219 if (*flash++ != 0xffffffff) {
1220 erased = 0;
1221 break;
1222 }
1223 }
wdenke65527f2004-02-12 00:47:09 +00001224
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001225 /* print empty and read-only info */
1226 printf (" %08lX %c %s ",
1227 info->start[i],
1228 erased ? 'E' : ' ',
1229 info->protect[i] ? "RO" : " ");
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001230#else /* ! CONFIG_SYS_FLASH_EMPTY_INFO */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001231 printf (" %08lX %s ",
1232 info->start[i],
1233 info->protect[i] ? "RO" : " ");
wdenke65527f2004-02-12 00:47:09 +00001234#endif
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001235 }
1236 putc ('\n');
1237 return;
wdenk2cefd152004-02-08 22:55:38 +00001238}
1239
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001240/*-----------------------------------------------------------------------
Jerry Van Barenaae73572008-03-08 13:48:01 -05001241 * This is used in a few places in write_buf() to show programming
1242 * progress. Making it a function is nasty because it needs to do side
1243 * effect updates to digit and dots. Repeated code is nasty too, so
1244 * we define it once here.
1245 */
Stefan Roese7758c162008-03-19 07:09:26 +01001246#ifdef CONFIG_FLASH_SHOW_PROGRESS
1247#define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub) \
Piotr Ziecik2a7493c2008-11-17 15:49:32 +01001248 if (flash_verbose) { \
1249 dots -= dots_sub; \
1250 if ((scale > 0) && (dots <= 0)) { \
1251 if ((digit % 5) == 0) \
1252 printf ("%d", digit / 5); \
1253 else \
1254 putc ('.'); \
1255 digit--; \
1256 dots += scale; \
1257 } \
Jerry Van Barenaae73572008-03-08 13:48:01 -05001258 }
Stefan Roese7758c162008-03-19 07:09:26 +01001259#else
1260#define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub)
1261#endif
Jerry Van Barenaae73572008-03-08 13:48:01 -05001262
1263/*-----------------------------------------------------------------------
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001264 * Copy memory to flash, returns:
1265 * 0 - OK
1266 * 1 - write timeout
1267 * 2 - Flash not erased
wdenk2cefd152004-02-08 22:55:38 +00001268 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001269int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
wdenk2cefd152004-02-08 22:55:38 +00001270{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001271 ulong wp;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001272 uchar *p;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001273 int aln;
wdenk2cefd152004-02-08 22:55:38 +00001274 cfiword_t cword;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001275 int i, rc;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001276#ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001277 int buffered_size;
wdenk2cefd152004-02-08 22:55:38 +00001278#endif
Jerry Van Barenaae73572008-03-08 13:48:01 -05001279#ifdef CONFIG_FLASH_SHOW_PROGRESS
1280 int digit = CONFIG_FLASH_SHOW_PROGRESS;
1281 int scale = 0;
1282 int dots = 0;
1283
1284 /*
1285 * Suppress if there are fewer than CONFIG_FLASH_SHOW_PROGRESS writes.
1286 */
1287 if (cnt >= CONFIG_FLASH_SHOW_PROGRESS) {
1288 scale = (int)((cnt + CONFIG_FLASH_SHOW_PROGRESS - 1) /
1289 CONFIG_FLASH_SHOW_PROGRESS);
1290 }
1291#endif
1292
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001293 /* get lower aligned address */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001294 wp = (addr & ~(info->portwidth - 1));
Haiying Wangc123a382007-02-21 16:52:31 +01001295
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001296 /* handle unaligned start */
1297 if ((aln = addr - wp) != 0) {
1298 cword.l = 0;
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001299 p = (uchar *)wp;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001300 for (i = 0; i < aln; ++i)
1301 flash_add_byte (info, &cword, flash_read8(p + i));
wdenk2cefd152004-02-08 22:55:38 +00001302
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001303 for (; (i < info->portwidth) && (cnt > 0); i++) {
1304 flash_add_byte (info, &cword, *src++);
1305 cnt--;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001306 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001307 for (; (cnt == 0) && (i < info->portwidth); ++i)
1308 flash_add_byte (info, &cword, flash_read8(p + i));
1309
1310 rc = flash_write_cfiword (info, wp, cword);
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001311 if (rc != 0)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001312 return rc;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001313
1314 wp += i;
Stefan Roese7758c162008-03-19 07:09:26 +01001315 FLASH_SHOW_PROGRESS(scale, dots, digit, i);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001316 }
1317
1318 /* handle the aligned part */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001319#ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001320 buffered_size = (info->portwidth / info->chipwidth);
1321 buffered_size *= info->buffer_size;
1322 while (cnt >= info->portwidth) {
1323 /* prohibit buffer write when buffer_size is 1 */
1324 if (info->buffer_size == 1) {
1325 cword.l = 0;
1326 for (i = 0; i < info->portwidth; i++)
1327 flash_add_byte (info, &cword, *src++);
1328 if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
1329 return rc;
1330 wp += info->portwidth;
1331 cnt -= info->portwidth;
1332 continue;
1333 }
1334
1335 /* write buffer until next buffered_size aligned boundary */
1336 i = buffered_size - (wp % buffered_size);
1337 if (i > cnt)
1338 i = cnt;
1339 if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK)
1340 return rc;
1341 i -= i & (info->portwidth - 1);
1342 wp += i;
1343 src += i;
1344 cnt -= i;
Stefan Roese7758c162008-03-19 07:09:26 +01001345 FLASH_SHOW_PROGRESS(scale, dots, digit, i);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001346 }
1347#else
1348 while (cnt >= info->portwidth) {
1349 cword.l = 0;
1350 for (i = 0; i < info->portwidth; i++) {
1351 flash_add_byte (info, &cword, *src++);
1352 }
1353 if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
1354 return rc;
1355 wp += info->portwidth;
1356 cnt -= info->portwidth;
Stefan Roese7758c162008-03-19 07:09:26 +01001357 FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001358 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001359#endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */
Jerry Van Barenaae73572008-03-08 13:48:01 -05001360
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001361 if (cnt == 0) {
1362 return (0);
1363 }
1364
1365 /*
1366 * handle unaligned tail bytes
1367 */
1368 cword.l = 0;
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001369 p = (uchar *)wp;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001370 for (i = 0; (i < info->portwidth) && (cnt > 0); ++i) {
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001371 flash_add_byte (info, &cword, *src++);
1372 --cnt;
1373 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001374 for (; i < info->portwidth; ++i)
1375 flash_add_byte (info, &cword, flash_read8(p + i));
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001376
1377 return flash_write_cfiword (info, wp, cword);
wdenk2cefd152004-02-08 22:55:38 +00001378}
wdenke65527f2004-02-12 00:47:09 +00001379
wdenk2cefd152004-02-08 22:55:38 +00001380/*-----------------------------------------------------------------------
1381 */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001382#ifdef CONFIG_SYS_FLASH_PROTECTION
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001383
1384int flash_real_protect (flash_info_t * info, long sector, int prot)
wdenk2cefd152004-02-08 22:55:38 +00001385{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001386 int retcode = 0;
wdenke65527f2004-02-12 00:47:09 +00001387
Rafael Campos13d2b612008-07-31 10:22:20 +02001388 switch (info->vendor) {
1389 case CFI_CMDSET_INTEL_PROG_REGIONS:
1390 case CFI_CMDSET_INTEL_STANDARD:
Nick Spenceec81b472008-08-19 22:21:16 -07001391 case CFI_CMDSET_INTEL_EXTENDED:
Rafael Campos13d2b612008-07-31 10:22:20 +02001392 flash_write_cmd (info, sector, 0,
1393 FLASH_CMD_CLEAR_STATUS);
1394 flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT);
1395 if (prot)
1396 flash_write_cmd (info, sector, 0,
1397 FLASH_CMD_PROTECT_SET);
1398 else
1399 flash_write_cmd (info, sector, 0,
1400 FLASH_CMD_PROTECT_CLEAR);
1401 break;
1402 case CFI_CMDSET_AMD_EXTENDED:
1403 case CFI_CMDSET_AMD_STANDARD:
Rafael Campos13d2b612008-07-31 10:22:20 +02001404 /* U-Boot only checks the first byte */
1405 if (info->manufacturer_id == (uchar)ATM_MANUFACT) {
1406 if (prot) {
1407 flash_unlock_seq (info, 0);
1408 flash_write_cmd (info, 0,
1409 info->addr_unlock1,
1410 ATM_CMD_SOFTLOCK_START);
1411 flash_unlock_seq (info, 0);
1412 flash_write_cmd (info, sector, 0,
1413 ATM_CMD_LOCK_SECT);
1414 } else {
1415 flash_write_cmd (info, 0,
1416 info->addr_unlock1,
1417 AMD_CMD_UNLOCK_START);
1418 if (info->device_id == ATM_ID_BV6416)
1419 flash_write_cmd (info, sector,
1420 0, ATM_CMD_UNLOCK_SECT);
1421 }
1422 }
1423 break;
TsiChung Liewb8c19292008-08-19 16:53:39 +00001424#ifdef CONFIG_FLASH_CFI_LEGACY
1425 case CFI_CMDSET_AMD_LEGACY:
1426 flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS);
1427 flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT);
1428 if (prot)
1429 flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_SET);
1430 else
1431 flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_CLEAR);
1432#endif
Rafael Campos13d2b612008-07-31 10:22:20 +02001433 };
wdenk2cefd152004-02-08 22:55:38 +00001434
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001435 if ((retcode =
1436 flash_full_status_check (info, sector, info->erase_blk_tout,
1437 prot ? "protect" : "unprotect")) == 0) {
wdenke65527f2004-02-12 00:47:09 +00001438
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001439 info->protect[sector] = prot;
1440
1441 /*
1442 * On some of Intel's flash chips (marked via legacy_unlock)
1443 * unprotect unprotects all locking.
1444 */
1445 if ((prot == 0) && (info->legacy_unlock)) {
1446 flash_sect_t i;
1447
1448 for (i = 0; i < info->sector_count; i++) {
1449 if (info->protect[i])
1450 flash_real_protect (info, i, 1);
1451 }
wdenk2cefd152004-02-08 22:55:38 +00001452 }
wdenk2cefd152004-02-08 22:55:38 +00001453 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001454 return retcode;
wdenk2cefd152004-02-08 22:55:38 +00001455}
wdenke65527f2004-02-12 00:47:09 +00001456
wdenk2cefd152004-02-08 22:55:38 +00001457/*-----------------------------------------------------------------------
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001458 * flash_read_user_serial - read the OneTimeProgramming cells
wdenk2cefd152004-02-08 22:55:38 +00001459 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001460void flash_read_user_serial (flash_info_t * info, void *buffer, int offset,
1461 int len)
wdenk2cefd152004-02-08 22:55:38 +00001462{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001463 uchar *src;
1464 uchar *dst;
wdenke65527f2004-02-12 00:47:09 +00001465
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001466 dst = buffer;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001467 src = flash_map (info, 0, FLASH_OFFSET_USER_PROTECTION);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001468 flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
1469 memcpy (dst, src + offset, len);
1470 flash_write_cmd (info, 0, 0, info->cmd_reset);
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001471 flash_unmap(info, 0, FLASH_OFFSET_USER_PROTECTION, src);
wdenk2cefd152004-02-08 22:55:38 +00001472}
1473
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001474/*
1475 * flash_read_factory_serial - read the device Id from the protection area
wdenk2cefd152004-02-08 22:55:38 +00001476 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001477void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset,
1478 int len)
wdenk2cefd152004-02-08 22:55:38 +00001479{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001480 uchar *src;
wdenke65527f2004-02-12 00:47:09 +00001481
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001482 src = flash_map (info, 0, FLASH_OFFSET_INTEL_PROTECTION);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001483 flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
1484 memcpy (buffer, src + offset, len);
1485 flash_write_cmd (info, 0, 0, info->cmd_reset);
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001486 flash_unmap(info, 0, FLASH_OFFSET_INTEL_PROTECTION, src);
wdenk2cefd152004-02-08 22:55:38 +00001487}
1488
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001489#endif /* CONFIG_SYS_FLASH_PROTECTION */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001490
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001491/*-----------------------------------------------------------------------
1492 * Reverse the order of the erase regions in the CFI QRY structure.
1493 * This is needed for chips that are either a) correctly detected as
1494 * top-boot, or b) buggy.
1495 */
1496static void cfi_reverse_geometry(struct cfi_qry *qry)
1497{
1498 unsigned int i, j;
1499 u32 tmp;
1500
1501 for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) {
1502 tmp = qry->erase_region_info[i];
1503 qry->erase_region_info[i] = qry->erase_region_info[j];
1504 qry->erase_region_info[j] = tmp;
1505 }
1506}
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001507
wdenk2cefd152004-02-08 22:55:38 +00001508/*-----------------------------------------------------------------------
Stefan Roese12797482006-11-13 13:55:24 +01001509 * read jedec ids from device and set corresponding fields in info struct
1510 *
1511 * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct
1512 *
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001513 */
1514static void cmdset_intel_read_jedec_ids(flash_info_t *info)
1515{
1516 flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
1517 flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID);
1518 udelay(1000); /* some flash are slow to respond */
1519 info->manufacturer_id = flash_read_uchar (info,
1520 FLASH_OFFSET_MANUFACTURER_ID);
1521 info->device_id = flash_read_uchar (info,
1522 FLASH_OFFSET_DEVICE_ID);
1523 flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
1524}
1525
1526static int cmdset_intel_init(flash_info_t *info, struct cfi_qry *qry)
1527{
1528 info->cmd_reset = FLASH_CMD_RESET;
1529
1530 cmdset_intel_read_jedec_ids(info);
1531 flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI);
1532
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001533#ifdef CONFIG_SYS_FLASH_PROTECTION
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001534 /* read legacy lock/unlock bit from intel flash */
1535 if (info->ext_addr) {
1536 info->legacy_unlock = flash_read_uchar (info,
1537 info->ext_addr + 5) & 0x08;
1538 }
1539#endif
1540
1541 return 0;
1542}
1543
1544static void cmdset_amd_read_jedec_ids(flash_info_t *info)
1545{
Niklaus Gigerf447f712009-07-22 17:13:24 +02001546 ushort bankId = 0;
1547 uchar manuId;
1548
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001549 flash_write_cmd(info, 0, 0, AMD_CMD_RESET);
1550 flash_unlock_seq(info, 0);
1551 flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID);
1552 udelay(1000); /* some flash are slow to respond */
Tor Krill7f2a3052008-03-28 11:29:10 +01001553
Niklaus Gigerf447f712009-07-22 17:13:24 +02001554 manuId = flash_read_uchar (info, FLASH_OFFSET_MANUFACTURER_ID);
1555 /* JEDEC JEP106Z specifies ID codes up to bank 7 */
1556 while (manuId == FLASH_CONTINUATION_CODE && bankId < 0x800) {
1557 bankId += 0x100;
1558 manuId = flash_read_uchar (info,
1559 bankId | FLASH_OFFSET_MANUFACTURER_ID);
1560 }
1561 info->manufacturer_id = manuId;
Tor Krill7f2a3052008-03-28 11:29:10 +01001562
1563 switch (info->chipwidth){
1564 case FLASH_CFI_8BIT:
1565 info->device_id = flash_read_uchar (info,
1566 FLASH_OFFSET_DEVICE_ID);
1567 if (info->device_id == 0x7E) {
1568 /* AMD 3-byte (expanded) device ids */
1569 info->device_id2 = flash_read_uchar (info,
1570 FLASH_OFFSET_DEVICE_ID2);
1571 info->device_id2 <<= 8;
1572 info->device_id2 |= flash_read_uchar (info,
1573 FLASH_OFFSET_DEVICE_ID3);
1574 }
1575 break;
1576 case FLASH_CFI_16BIT:
1577 info->device_id = flash_read_word (info,
1578 FLASH_OFFSET_DEVICE_ID);
1579 break;
1580 default:
1581 break;
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001582 }
1583 flash_write_cmd(info, 0, 0, AMD_CMD_RESET);
1584}
1585
1586static int cmdset_amd_init(flash_info_t *info, struct cfi_qry *qry)
1587{
1588 info->cmd_reset = AMD_CMD_RESET;
1589
1590 cmdset_amd_read_jedec_ids(info);
1591 flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI);
1592
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001593 return 0;
1594}
1595
1596#ifdef CONFIG_FLASH_CFI_LEGACY
Stefan Roese12797482006-11-13 13:55:24 +01001597static void flash_read_jedec_ids (flash_info_t * info)
1598{
1599 info->manufacturer_id = 0;
1600 info->device_id = 0;
1601 info->device_id2 = 0;
1602
1603 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001604 case CFI_CMDSET_INTEL_PROG_REGIONS:
Stefan Roese12797482006-11-13 13:55:24 +01001605 case CFI_CMDSET_INTEL_STANDARD:
1606 case CFI_CMDSET_INTEL_EXTENDED:
Michael Schwingen5fb0aa42008-01-12 20:29:47 +01001607 cmdset_intel_read_jedec_ids(info);
Stefan Roese12797482006-11-13 13:55:24 +01001608 break;
1609 case CFI_CMDSET_AMD_STANDARD:
1610 case CFI_CMDSET_AMD_EXTENDED:
Michael Schwingen5fb0aa42008-01-12 20:29:47 +01001611 cmdset_amd_read_jedec_ids(info);
Stefan Roese12797482006-11-13 13:55:24 +01001612 break;
1613 default:
1614 break;
1615 }
1616}
1617
1618/*-----------------------------------------------------------------------
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001619 * Call board code to request info about non-CFI flash.
1620 * board_flash_get_legacy needs to fill in at least:
1621 * info->portwidth, info->chipwidth and info->interface for Jedec probing.
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001622 */
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001623static int flash_detect_legacy(phys_addr_t base, int banknum)
wdenk2cefd152004-02-08 22:55:38 +00001624{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001625 flash_info_t *info = &flash_info[banknum];
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001626
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001627 if (board_flash_get_legacy(base, banknum, info)) {
1628 /* board code may have filled info completely. If not, we
1629 use JEDEC ID probing. */
1630 if (!info->vendor) {
1631 int modes[] = {
1632 CFI_CMDSET_AMD_STANDARD,
1633 CFI_CMDSET_INTEL_STANDARD
1634 };
1635 int i;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001636
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001637 for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) {
1638 info->vendor = modes[i];
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001639 info->start[0] =
1640 (ulong)map_physmem(base,
Stefan Roeseb8443702009-02-05 11:44:52 +01001641 info->portwidth,
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001642 MAP_NOCACHE);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001643 if (info->portwidth == FLASH_CFI_8BIT
1644 && info->interface == FLASH_CFI_X8X16) {
1645 info->addr_unlock1 = 0x2AAA;
1646 info->addr_unlock2 = 0x5555;
1647 } else {
1648 info->addr_unlock1 = 0x5555;
1649 info->addr_unlock2 = 0x2AAA;
1650 }
1651 flash_read_jedec_ids(info);
1652 debug("JEDEC PROBE: ID %x %x %x\n",
1653 info->manufacturer_id,
1654 info->device_id,
1655 info->device_id2);
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001656 if (jedec_flash_match(info, info->start[0]))
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001657 break;
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001658 else
Stefan Roeseb8443702009-02-05 11:44:52 +01001659 unmap_physmem((void *)info->start[0],
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001660 MAP_NOCACHE);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001661 }
1662 }
1663
1664 switch(info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001665 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001666 case CFI_CMDSET_INTEL_STANDARD:
1667 case CFI_CMDSET_INTEL_EXTENDED:
1668 info->cmd_reset = FLASH_CMD_RESET;
1669 break;
1670 case CFI_CMDSET_AMD_STANDARD:
1671 case CFI_CMDSET_AMD_EXTENDED:
1672 case CFI_CMDSET_AMD_LEGACY:
1673 info->cmd_reset = AMD_CMD_RESET;
1674 break;
1675 }
1676 info->flash_id = FLASH_MAN_CFI;
1677 return 1;
1678 }
1679 return 0; /* use CFI */
1680}
1681#else
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001682static inline int flash_detect_legacy(phys_addr_t base, int banknum)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001683{
1684 return 0; /* use CFI */
1685}
1686#endif
1687
1688/*-----------------------------------------------------------------------
1689 * detect if flash is compatible with the Common Flash Interface (CFI)
1690 * http://www.jedec.org/download/search/jesd68.pdf
1691 */
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001692static void flash_read_cfi (flash_info_t *info, void *buf,
1693 unsigned int start, size_t len)
1694{
1695 u8 *p = buf;
1696 unsigned int i;
1697
1698 for (i = 0; i < len; i++)
1699 p[i] = flash_read_uchar(info, start + i);
1700}
1701
1702static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001703{
1704 int cfi_offset;
1705
Michael Schwingen4661cf72008-02-18 23:16:35 +01001706 /* We do not yet know what kind of commandset to use, so we issue
1707 the reset command in both Intel and AMD variants, in the hope
1708 that AMD flash roms ignore the Intel command. */
1709 flash_write_cmd (info, 0, 0, AMD_CMD_RESET);
1710 flash_write_cmd (info, 0, 0, FLASH_CMD_RESET);
1711
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001712 for (cfi_offset=0;
1713 cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint);
1714 cfi_offset++) {
1715 flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],
1716 FLASH_CMD_CFI);
1717 if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q')
1718 && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R')
1719 && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) {
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001720 flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,
1721 sizeof(struct cfi_qry));
1722 info->interface = le16_to_cpu(qry->interface_desc);
1723
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001724 info->cfi_offset = flash_offset_cfi[cfi_offset];
1725 debug ("device interface is %d\n",
1726 info->interface);
1727 debug ("found port %d chip %d ",
1728 info->portwidth, info->chipwidth);
1729 debug ("port %d bits chip %d bits\n",
1730 info->portwidth << CFI_FLASH_SHIFT_WIDTH,
1731 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
1732
1733 /* calculate command offsets as in the Linux driver */
1734 info->addr_unlock1 = 0x555;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001735 info->addr_unlock2 = 0x2aa;
1736
1737 /*
1738 * modify the unlock address if we are
1739 * in compatibility mode
1740 */
1741 if ( /* x8/x16 in x8 mode */
1742 ((info->chipwidth == FLASH_CFI_BY8) &&
1743 (info->interface == FLASH_CFI_X8X16)) ||
1744 /* x16/x32 in x16 mode */
1745 ((info->chipwidth == FLASH_CFI_BY16) &&
1746 (info->interface == FLASH_CFI_X16X32)))
1747 {
1748 info->addr_unlock1 = 0xaaa;
1749 info->addr_unlock2 = 0x555;
1750 }
1751
1752 info->name = "CFI conformant";
1753 return 1;
1754 }
1755 }
1756
1757 return 0;
1758}
1759
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001760static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001761{
wdenke65527f2004-02-12 00:47:09 +00001762 debug ("flash detect cfi\n");
wdenk2cefd152004-02-08 22:55:38 +00001763
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001764 for (info->portwidth = CONFIG_SYS_FLASH_CFI_WIDTH;
wdenke65527f2004-02-12 00:47:09 +00001765 info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) {
1766 for (info->chipwidth = FLASH_CFI_BY8;
1767 info->chipwidth <= info->portwidth;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001768 info->chipwidth <<= 1)
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001769 if (__flash_detect_cfi(info, qry))
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001770 return 1;
wdenk2cefd152004-02-08 22:55:38 +00001771 }
wdenke65527f2004-02-12 00:47:09 +00001772 debug ("not found\n");
wdenk2cefd152004-02-08 22:55:38 +00001773 return 0;
1774}
wdenke65527f2004-02-12 00:47:09 +00001775
wdenk2cefd152004-02-08 22:55:38 +00001776/*
Haavard Skinnemoen750ea7f2007-12-14 15:36:18 +01001777 * Manufacturer-specific quirks. Add workarounds for geometry
1778 * reversal, etc. here.
1779 */
1780static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry)
1781{
1782 /* check if flash geometry needs reversal */
1783 if (qry->num_erase_regions > 1) {
1784 /* reverse geometry if top boot part */
1785 if (info->cfi_version < 0x3131) {
1786 /* CFI < 1.1, try to guess from device id */
1787 if ((info->device_id & 0x80) != 0)
1788 cfi_reverse_geometry(qry);
1789 } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) {
1790 /* CFI >= 1.1, deduct from top/bottom flag */
1791 /* note: ext_addr is valid since cfi_version > 0 */
1792 cfi_reverse_geometry(qry);
1793 }
1794 }
1795}
1796
1797static void flash_fixup_atmel(flash_info_t *info, struct cfi_qry *qry)
1798{
1799 int reverse_geometry = 0;
1800
1801 /* Check the "top boot" bit in the PRI */
1802 if (info->ext_addr && !(flash_read_uchar(info, info->ext_addr + 6) & 1))
1803 reverse_geometry = 1;
1804
1805 /* AT49BV6416(T) list the erase regions in the wrong order.
1806 * However, the device ID is identical with the non-broken
Ulf Samuelsson07f9b4e2009-03-27 23:26:43 +01001807 * AT49BV642D they differ in the high byte.
Haavard Skinnemoen750ea7f2007-12-14 15:36:18 +01001808 */
Haavard Skinnemoen750ea7f2007-12-14 15:36:18 +01001809 if (info->device_id == 0xd6 || info->device_id == 0xd2)
1810 reverse_geometry = !reverse_geometry;
Haavard Skinnemoen750ea7f2007-12-14 15:36:18 +01001811
1812 if (reverse_geometry)
1813 cfi_reverse_geometry(qry);
1814}
1815
Richard Retanubunbe4dca22009-01-14 08:44:26 -05001816static void flash_fixup_stm(flash_info_t *info, struct cfi_qry *qry)
1817{
1818 /* check if flash geometry needs reversal */
1819 if (qry->num_erase_regions > 1) {
1820 /* reverse geometry if top boot part */
1821 if (info->cfi_version < 0x3131) {
Richard Retanubun47255a52009-03-06 10:09:37 -05001822 /* CFI < 1.1, guess by device id (M29W320{DT,ET} only) */
1823 if (info->device_id == 0x22CA ||
1824 info->device_id == 0x2256) {
Richard Retanubunbe4dca22009-01-14 08:44:26 -05001825 cfi_reverse_geometry(qry);
1826 }
1827 }
1828 }
1829}
1830
Haavard Skinnemoen750ea7f2007-12-14 15:36:18 +01001831/*
wdenk2cefd152004-02-08 22:55:38 +00001832 * The following code cannot be run from FLASH!
1833 *
1834 */
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001835ulong flash_get_size (phys_addr_t base, int banknum)
wdenk2cefd152004-02-08 22:55:38 +00001836{
wdenke65527f2004-02-12 00:47:09 +00001837 flash_info_t *info = &flash_info[banknum];
wdenk2cefd152004-02-08 22:55:38 +00001838 int i, j;
1839 flash_sect_t sect_cnt;
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001840 phys_addr_t sector;
wdenk2cefd152004-02-08 22:55:38 +00001841 unsigned long tmp;
1842 int size_ratio;
1843 uchar num_erase_regions;
wdenke65527f2004-02-12 00:47:09 +00001844 int erase_region_size;
1845 int erase_region_count;
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001846 struct cfi_qry qry;
Stefan Roese12797482006-11-13 13:55:24 +01001847
Kumar Gala899032b2008-05-15 15:13:08 -05001848 memset(&qry, 0, sizeof(qry));
1849
Stefan Roese12797482006-11-13 13:55:24 +01001850 info->ext_addr = 0;
1851 info->cfi_version = 0;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001852#ifdef CONFIG_SYS_FLASH_PROTECTION
Stefan Roeseefef95b2006-04-01 13:41:03 +02001853 info->legacy_unlock = 0;
1854#endif
wdenk2cefd152004-02-08 22:55:38 +00001855
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001856 info->start[0] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE);
wdenk2cefd152004-02-08 22:55:38 +00001857
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001858 if (flash_detect_cfi (info, &qry)) {
1859 info->vendor = le16_to_cpu(qry.p_id);
1860 info->ext_addr = le16_to_cpu(qry.p_adr);
1861 num_erase_regions = qry.num_erase_regions;
1862
Stefan Roese12797482006-11-13 13:55:24 +01001863 if (info->ext_addr) {
1864 info->cfi_version = (ushort) flash_read_uchar (info,
1865 info->ext_addr + 3) << 8;
1866 info->cfi_version |= (ushort) flash_read_uchar (info,
1867 info->ext_addr + 4);
1868 }
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001869
wdenke65527f2004-02-12 00:47:09 +00001870#ifdef DEBUG
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001871 flash_printqry (&qry);
wdenke65527f2004-02-12 00:47:09 +00001872#endif
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001873
wdenke65527f2004-02-12 00:47:09 +00001874 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001875 case CFI_CMDSET_INTEL_PROG_REGIONS:
wdenk2cefd152004-02-08 22:55:38 +00001876 case CFI_CMDSET_INTEL_STANDARD:
1877 case CFI_CMDSET_INTEL_EXTENDED:
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001878 cmdset_intel_init(info, &qry);
wdenk2cefd152004-02-08 22:55:38 +00001879 break;
1880 case CFI_CMDSET_AMD_STANDARD:
1881 case CFI_CMDSET_AMD_EXTENDED:
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001882 cmdset_amd_init(info, &qry);
wdenk2cefd152004-02-08 22:55:38 +00001883 break;
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001884 default:
1885 printf("CFI: Unknown command set 0x%x\n",
1886 info->vendor);
1887 /*
1888 * Unfortunately, this means we don't know how
1889 * to get the chip back to Read mode. Might
1890 * as well try an Intel-style reset...
1891 */
1892 flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
1893 return 0;
wdenk2cefd152004-02-08 22:55:38 +00001894 }
wdenk6cfa84e2004-02-10 00:03:41 +00001895
Haavard Skinnemoen750ea7f2007-12-14 15:36:18 +01001896 /* Do manufacturer-specific fixups */
1897 switch (info->manufacturer_id) {
1898 case 0x0001:
1899 flash_fixup_amd(info, &qry);
1900 break;
1901 case 0x001f:
1902 flash_fixup_atmel(info, &qry);
1903 break;
Richard Retanubunbe4dca22009-01-14 08:44:26 -05001904 case 0x0020:
1905 flash_fixup_stm(info, &qry);
1906 break;
Haavard Skinnemoen750ea7f2007-12-14 15:36:18 +01001907 }
1908
wdenke65527f2004-02-12 00:47:09 +00001909 debug ("manufacturer is %d\n", info->vendor);
Stefan Roese12797482006-11-13 13:55:24 +01001910 debug ("manufacturer id is 0x%x\n", info->manufacturer_id);
1911 debug ("device id is 0x%x\n", info->device_id);
1912 debug ("device id2 is 0x%x\n", info->device_id2);
1913 debug ("cfi version is 0x%04x\n", info->cfi_version);
1914
wdenk2cefd152004-02-08 22:55:38 +00001915 size_ratio = info->portwidth / info->chipwidth;
wdenke65527f2004-02-12 00:47:09 +00001916 /* if the chip is x8/x16 reduce the ratio by half */
1917 if ((info->interface == FLASH_CFI_X8X16)
1918 && (info->chipwidth == FLASH_CFI_BY8)) {
1919 size_ratio >>= 1;
1920 }
wdenke65527f2004-02-12 00:47:09 +00001921 debug ("size_ratio %d port %d bits chip %d bits\n",
1922 size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH,
1923 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
1924 debug ("found %d erase regions\n", num_erase_regions);
wdenk2cefd152004-02-08 22:55:38 +00001925 sect_cnt = 0;
1926 sector = base;
wdenke65527f2004-02-12 00:47:09 +00001927 for (i = 0; i < num_erase_regions; i++) {
1928 if (i > NUM_ERASE_REGIONS) {
wdenke537b3b2004-02-23 23:54:43 +00001929 printf ("%d erase regions found, only %d used\n",
1930 num_erase_regions, NUM_ERASE_REGIONS);
wdenk2cefd152004-02-08 22:55:38 +00001931 break;
1932 }
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001933
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001934 tmp = le32_to_cpu(qry.erase_region_info[i]);
1935 debug("erase region %u: 0x%08lx\n", i, tmp);
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001936
1937 erase_region_count = (tmp & 0xffff) + 1;
1938 tmp >>= 16;
wdenke65527f2004-02-12 00:47:09 +00001939 erase_region_size =
1940 (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128;
wdenkaeba06f2004-06-09 17:34:58 +00001941 debug ("erase_region_count = %d erase_region_size = %d\n",
wdenke537b3b2004-02-23 23:54:43 +00001942 erase_region_count, erase_region_size);
wdenke65527f2004-02-12 00:47:09 +00001943 for (j = 0; j < erase_region_count; j++) {
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001944 if (sect_cnt >= CONFIG_SYS_MAX_FLASH_SECT) {
Michael Schwingen73d044d2007-12-07 23:35:02 +01001945 printf("ERROR: too many flash sectors\n");
1946 break;
1947 }
Becky Bruce9d1f6af2009-02-02 16:34:51 -06001948 info->start[sect_cnt] =
1949 (ulong)map_physmem(sector,
1950 info->portwidth,
1951 MAP_NOCACHE);
wdenk2cefd152004-02-08 22:55:38 +00001952 sector += (erase_region_size * size_ratio);
wdenk26c58432005-01-09 17:12:27 +00001953
1954 /*
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001955 * Only read protection status from
1956 * supported devices (intel...)
wdenk26c58432005-01-09 17:12:27 +00001957 */
1958 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001959 case CFI_CMDSET_INTEL_PROG_REGIONS:
wdenk26c58432005-01-09 17:12:27 +00001960 case CFI_CMDSET_INTEL_EXTENDED:
1961 case CFI_CMDSET_INTEL_STANDARD:
1962 info->protect[sect_cnt] =
1963 flash_isset (info, sect_cnt,
1964 FLASH_OFFSET_PROTECT,
1965 FLASH_STATUS_PROTECT);
1966 break;
1967 default:
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001968 /* default: not protected */
1969 info->protect[sect_cnt] = 0;
wdenk26c58432005-01-09 17:12:27 +00001970 }
1971
wdenk2cefd152004-02-08 22:55:38 +00001972 sect_cnt++;
1973 }
1974 }
1975
1976 info->sector_count = sect_cnt;
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001977 info->size = 1 << qry.dev_size;
wdenk2cefd152004-02-08 22:55:38 +00001978 /* multiply the size by the number of chips */
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001979 info->size *= size_ratio;
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001980 info->buffer_size = 1 << le16_to_cpu(qry.max_buf_write_size);
1981 tmp = 1 << qry.block_erase_timeout_typ;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001982 info->erase_blk_tout = tmp *
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001983 (1 << qry.block_erase_timeout_max);
1984 tmp = (1 << qry.buf_write_timeout_typ) *
1985 (1 << qry.buf_write_timeout_max);
1986
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001987 /* round up when converting to ms */
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001988 info->buffer_write_tout = (tmp + 999) / 1000;
1989 tmp = (1 << qry.word_write_timeout_typ) *
1990 (1 << qry.word_write_timeout_max);
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001991 /* round up when converting to ms */
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001992 info->write_tout = (tmp + 999) / 1000;
wdenk2cefd152004-02-08 22:55:38 +00001993 info->flash_id = FLASH_MAN_CFI;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001994 if ((info->interface == FLASH_CFI_X8X16) &&
1995 (info->chipwidth == FLASH_CFI_BY8)) {
1996 /* XXX - Need to test on x8/x16 in parallel. */
1997 info->portwidth >>= 1;
wdenked2ac4b2004-03-14 18:23:55 +00001998 }
Mike Frysinger59404ee2008-10-02 01:55:38 -04001999
2000 flash_write_cmd (info, 0, 0, info->cmd_reset);
wdenk2cefd152004-02-08 22:55:38 +00002001 }
2002
wdenke65527f2004-02-12 00:47:09 +00002003 return (info->size);
wdenk2cefd152004-02-08 22:55:38 +00002004}
2005
Piotr Ziecik2a7493c2008-11-17 15:49:32 +01002006void flash_set_verbose(uint v)
2007{
2008 flash_verbose = v;
2009}
2010
wdenk2cefd152004-02-08 22:55:38 +00002011/*-----------------------------------------------------------------------
2012 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002013unsigned long flash_init (void)
wdenk2cefd152004-02-08 22:55:38 +00002014{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002015 unsigned long size = 0;
2016 int i;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002017#if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST)
Matthias Fuchs50431522008-04-18 16:29:40 +02002018 struct apl_s {
2019 ulong start;
2020 ulong size;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002021 } apl[] = CONFIG_SYS_FLASH_AUTOPROTECT_LIST;
Matthias Fuchs50431522008-04-18 16:29:40 +02002022#endif
wdenk2cefd152004-02-08 22:55:38 +00002023
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002024#ifdef CONFIG_SYS_FLASH_PROTECTION
Eric Schumann8f7ee7d2009-03-21 09:59:34 -04002025 /* read environment from EEPROM */
2026 char s[64];
2027 getenv_r ("unlock", s, sizeof(s));
Michael Schwingen73d044d2007-12-07 23:35:02 +01002028#endif
wdenk2cefd152004-02-08 22:55:38 +00002029
Becky Bruce9d1f6af2009-02-02 16:34:51 -06002030#define BANK_BASE(i) (((phys_addr_t [CFI_MAX_FLASH_BANKS])CONFIG_SYS_FLASH_BANKS_LIST)[i])
Wolfgang Denk9f5fb0f2008-08-08 16:39:54 +02002031
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002032 /* Init: no FLASHes known */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002033 for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002034 flash_info[i].flash_id = FLASH_UNKNOWN;
wdenk2cefd152004-02-08 22:55:38 +00002035
Wolfgang Denk9f5fb0f2008-08-08 16:39:54 +02002036 if (!flash_detect_legacy (BANK_BASE(i), i))
2037 flash_get_size (BANK_BASE(i), i);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002038 size += flash_info[i].size;
2039 if (flash_info[i].flash_id == FLASH_UNKNOWN) {
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002040#ifndef CONFIG_SYS_FLASH_QUIET_TEST
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002041 printf ("## Unknown FLASH on Bank %d "
2042 "- Size = 0x%08lx = %ld MB\n",
2043 i+1, flash_info[i].size,
2044 flash_info[i].size << 20);
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002045#endif /* CONFIG_SYS_FLASH_QUIET_TEST */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002046 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002047#ifdef CONFIG_SYS_FLASH_PROTECTION
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002048 else if ((s != NULL) && (strcmp(s, "yes") == 0)) {
2049 /*
2050 * Only the U-Boot image and it's environment
2051 * is protected, all other sectors are
2052 * unprotected (unlocked) if flash hardware
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002053 * protection is used (CONFIG_SYS_FLASH_PROTECTION)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002054 * and the environment variable "unlock" is
2055 * set to "yes".
2056 */
2057 if (flash_info[i].legacy_unlock) {
2058 int k;
wdenk2cefd152004-02-08 22:55:38 +00002059
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002060 /*
2061 * Disable legacy_unlock temporarily,
2062 * since flash_real_protect would
2063 * relock all other sectors again
2064 * otherwise.
2065 */
2066 flash_info[i].legacy_unlock = 0;
wdenk2cefd152004-02-08 22:55:38 +00002067
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002068 /*
2069 * Legacy unlocking (e.g. Intel J3) ->
2070 * unlock only one sector. This will
2071 * unlock all sectors.
2072 */
2073 flash_real_protect (&flash_info[i], 0, 0);
wdenk2cefd152004-02-08 22:55:38 +00002074
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002075 flash_info[i].legacy_unlock = 1;
wdenk2cefd152004-02-08 22:55:38 +00002076
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002077 /*
2078 * Manually mark other sectors as
2079 * unlocked (unprotected)
2080 */
2081 for (k = 1; k < flash_info[i].sector_count; k++)
2082 flash_info[i].protect[k] = 0;
2083 } else {
2084 /*
2085 * No legancy unlocking -> unlock all sectors
2086 */
2087 flash_protect (FLAG_PROTECT_CLEAR,
2088 flash_info[i].start[0],
2089 flash_info[i].start[0]
2090 + flash_info[i].size - 1,
2091 &flash_info[i]);
Stefan Roesec865e6c2006-02-28 15:29:58 +01002092 }
Stefan Roesec865e6c2006-02-28 15:29:58 +01002093 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002094#endif /* CONFIG_SYS_FLASH_PROTECTION */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002095 }
Stefan Roesec865e6c2006-02-28 15:29:58 +01002096
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002097 /* Monitor protection ON by default */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002098#if (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002099 flash_protect (FLAG_PROTECT_SET,
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002100 CONFIG_SYS_MONITOR_BASE,
2101 CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1,
2102 flash_get_info(CONFIG_SYS_MONITOR_BASE));
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002103#endif
Stefan Roesec865e6c2006-02-28 15:29:58 +01002104
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002105 /* Environment protection ON by default */
Jean-Christophe PLAGNIOL-VILLARD53db4cd2008-09-10 22:48:04 +02002106#ifdef CONFIG_ENV_IS_IN_FLASH
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002107 flash_protect (FLAG_PROTECT_SET,
Jean-Christophe PLAGNIOL-VILLARD7e1cda62008-09-10 22:48:06 +02002108 CONFIG_ENV_ADDR,
2109 CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1,
2110 flash_get_info(CONFIG_ENV_ADDR));
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002111#endif
Stefan Roesec865e6c2006-02-28 15:29:58 +01002112
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002113 /* Redundant environment protection ON by default */
Jean-Christophe PLAGNIOL-VILLARD7e1cda62008-09-10 22:48:06 +02002114#ifdef CONFIG_ENV_ADDR_REDUND
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002115 flash_protect (FLAG_PROTECT_SET,
Jean-Christophe PLAGNIOL-VILLARD7e1cda62008-09-10 22:48:06 +02002116 CONFIG_ENV_ADDR_REDUND,
Wolfgang Denk47913832009-05-15 00:16:03 +02002117 CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SECT_SIZE - 1,
Jean-Christophe PLAGNIOL-VILLARD7e1cda62008-09-10 22:48:06 +02002118 flash_get_info(CONFIG_ENV_ADDR_REDUND));
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002119#endif
Matthias Fuchs50431522008-04-18 16:29:40 +02002120
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02002121#if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST)
Matthias Fuchs50431522008-04-18 16:29:40 +02002122 for (i = 0; i < (sizeof(apl) / sizeof(struct apl_s)); i++) {
2123 debug("autoprotecting from %08x to %08x\n",
2124 apl[i].start, apl[i].start + apl[i].size - 1);
2125 flash_protect (FLAG_PROTECT_SET,
2126 apl[i].start,
2127 apl[i].start + apl[i].size - 1,
2128 flash_get_info(apl[i].start));
2129 }
2130#endif
Piotr Ziecik3e939e92008-11-17 15:57:58 +01002131
2132#ifdef CONFIG_FLASH_CFI_MTD
2133 cfi_mtd_init();
2134#endif
2135
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002136 return (size);
wdenk2cefd152004-02-08 22:55:38 +00002137}