blob: 12647ef98613ff818be19e53fd33e65eaf90c445 [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>
wdenke65527f2004-02-12 00:47:09 +000042#ifdef CFG_FLASH_CFI_DRIVER
wdenke537b3b2004-02-23 23:54:43 +000043
wdenk2cefd152004-02-08 22:55:38 +000044/*
Haavard Skinnemoend523e392007-12-13 12:56:28 +010045 * This file implements a Common Flash Interface (CFI) driver for
46 * U-Boot.
47 *
48 * The width of the port and the width of the chips are determined at
49 * initialization. These widths are used to calculate the address for
50 * access CFI data structures.
wdenk2cefd152004-02-08 22:55:38 +000051 *
52 * References
53 * JEDEC Standard JESD68 - Common Flash Interface (CFI)
54 * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes
55 * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets
56 * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet
Stefan Roese12797482006-11-13 13:55:24 +010057 * AMD CFI Specification, Release 2.0 December 1, 2001
58 * AMD/Spansion Application Note: Migration from Single-byte to Three-byte
59 * Device IDs, Publication Number 25538 Revision A, November 8, 2001
wdenk2cefd152004-02-08 22:55:38 +000060 *
Haavard Skinnemoend523e392007-12-13 12:56:28 +010061 * Define CFG_WRITE_SWAPPED_DATA, if you have to swap the Bytes between
Heiko Schocher800db312007-01-19 18:05:26 +010062 * reading and writing ... (yes there is such a Hardware).
wdenk2cefd152004-02-08 22:55:38 +000063 */
64
wdenke65527f2004-02-12 00:47:09 +000065#ifndef CFG_FLASH_BANKS_LIST
66#define CFG_FLASH_BANKS_LIST { CFG_FLASH_BASE }
67#endif
68
wdenk2cefd152004-02-08 22:55:38 +000069#define FLASH_CMD_CFI 0x98
70#define FLASH_CMD_READ_ID 0x90
71#define FLASH_CMD_RESET 0xff
72#define FLASH_CMD_BLOCK_ERASE 0x20
73#define FLASH_CMD_ERASE_CONFIRM 0xD0
74#define FLASH_CMD_WRITE 0x40
75#define FLASH_CMD_PROTECT 0x60
76#define FLASH_CMD_PROTECT_SET 0x01
77#define FLASH_CMD_PROTECT_CLEAR 0xD0
78#define FLASH_CMD_CLEAR_STATUS 0x50
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +040079#define FLASH_CMD_READ_STATUS 0x70
wdenke65527f2004-02-12 00:47:09 +000080#define FLASH_CMD_WRITE_TO_BUFFER 0xE8
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +040081#define FLASH_CMD_WRITE_BUFFER_PROG 0xE9
wdenke65527f2004-02-12 00:47:09 +000082#define FLASH_CMD_WRITE_BUFFER_CONFIRM 0xD0
wdenk2cefd152004-02-08 22:55:38 +000083
84#define FLASH_STATUS_DONE 0x80
85#define FLASH_STATUS_ESS 0x40
86#define FLASH_STATUS_ECLBS 0x20
87#define FLASH_STATUS_PSLBS 0x10
88#define FLASH_STATUS_VPENS 0x08
89#define FLASH_STATUS_PSS 0x04
90#define FLASH_STATUS_DPS 0x02
91#define FLASH_STATUS_R 0x01
92#define FLASH_STATUS_PROTECT 0x01
93
94#define AMD_CMD_RESET 0xF0
95#define AMD_CMD_WRITE 0xA0
96#define AMD_CMD_ERASE_START 0x80
97#define AMD_CMD_ERASE_SECTOR 0x30
wdenked2ac4b2004-03-14 18:23:55 +000098#define AMD_CMD_UNLOCK_START 0xAA
99#define AMD_CMD_UNLOCK_ACK 0x55
Stefan Roesec865e6c2006-02-28 15:29:58 +0100100#define AMD_CMD_WRITE_TO_BUFFER 0x25
101#define AMD_CMD_WRITE_BUFFER_CONFIRM 0x29
wdenk2cefd152004-02-08 22:55:38 +0000102
103#define AMD_STATUS_TOGGLE 0x40
104#define AMD_STATUS_ERROR 0x20
Stefan Roesec865e6c2006-02-28 15:29:58 +0100105
Stefan Roese12797482006-11-13 13:55:24 +0100106#define FLASH_OFFSET_MANUFACTURER_ID 0x00
107#define FLASH_OFFSET_DEVICE_ID 0x01
108#define FLASH_OFFSET_DEVICE_ID2 0x0E
109#define FLASH_OFFSET_DEVICE_ID3 0x0F
wdenk2cefd152004-02-08 22:55:38 +0000110#define FLASH_OFFSET_CFI 0x55
Wolfgang Denkafa0dd02006-12-27 01:26:13 +0100111#define FLASH_OFFSET_CFI_ALT 0x555
wdenk2cefd152004-02-08 22:55:38 +0000112#define FLASH_OFFSET_CFI_RESP 0x10
wdenke65527f2004-02-12 00:47:09 +0000113#define FLASH_OFFSET_PRIMARY_VENDOR 0x13
Haavard Skinnemoend523e392007-12-13 12:56:28 +0100114/* extended query table primary address */
115#define FLASH_OFFSET_EXT_QUERY_T_P_ADDR 0x15
wdenk2cefd152004-02-08 22:55:38 +0000116#define FLASH_OFFSET_WTOUT 0x1F
wdenke65527f2004-02-12 00:47:09 +0000117#define FLASH_OFFSET_WBTOUT 0x20
wdenk2cefd152004-02-08 22:55:38 +0000118#define FLASH_OFFSET_ETOUT 0x21
wdenke65527f2004-02-12 00:47:09 +0000119#define FLASH_OFFSET_CETOUT 0x22
wdenk2cefd152004-02-08 22:55:38 +0000120#define FLASH_OFFSET_WMAX_TOUT 0x23
wdenke65527f2004-02-12 00:47:09 +0000121#define FLASH_OFFSET_WBMAX_TOUT 0x24
wdenk2cefd152004-02-08 22:55:38 +0000122#define FLASH_OFFSET_EMAX_TOUT 0x25
wdenke65527f2004-02-12 00:47:09 +0000123#define FLASH_OFFSET_CEMAX_TOUT 0x26
wdenk2cefd152004-02-08 22:55:38 +0000124#define FLASH_OFFSET_SIZE 0x27
wdenke65527f2004-02-12 00:47:09 +0000125#define FLASH_OFFSET_INTERFACE 0x28
126#define FLASH_OFFSET_BUFFER_SIZE 0x2A
wdenk2cefd152004-02-08 22:55:38 +0000127#define FLASH_OFFSET_NUM_ERASE_REGIONS 0x2C
128#define FLASH_OFFSET_ERASE_REGIONS 0x2D
129#define FLASH_OFFSET_PROTECT 0x02
wdenke65527f2004-02-12 00:47:09 +0000130#define FLASH_OFFSET_USER_PROTECTION 0x85
131#define FLASH_OFFSET_INTEL_PROTECTION 0x81
wdenk2cefd152004-02-08 22:55:38 +0000132
Stefan Roese12797482006-11-13 13:55:24 +0100133#define CFI_CMDSET_NONE 0
134#define CFI_CMDSET_INTEL_EXTENDED 1
135#define CFI_CMDSET_AMD_STANDARD 2
136#define CFI_CMDSET_INTEL_STANDARD 3
137#define CFI_CMDSET_AMD_EXTENDED 4
138#define CFI_CMDSET_MITSU_STANDARD 256
139#define CFI_CMDSET_MITSU_EXTENDED 257
140#define CFI_CMDSET_SST 258
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400141#define CFI_CMDSET_INTEL_PROG_REGIONS 512
wdenk2cefd152004-02-08 22:55:38 +0000142
wdenk51242782004-12-18 22:35:43 +0000143#ifdef CFG_FLASH_CFI_AMD_RESET /* needed for STM_ID_29W320DB on UC100 */
144# undef FLASH_CMD_RESET
Stefan Roese12797482006-11-13 13:55:24 +0100145# define FLASH_CMD_RESET AMD_CMD_RESET /* use AMD-Reset instead */
wdenk51242782004-12-18 22:35:43 +0000146#endif
147
wdenk2cefd152004-02-08 22:55:38 +0000148typedef union {
149 unsigned char c;
150 unsigned short w;
151 unsigned long l;
152 unsigned long long ll;
153} cfiword_t;
154
Stefan Roese12797482006-11-13 13:55:24 +0100155#define NUM_ERASE_REGIONS 4 /* max. number of erase regions */
wdenk2cefd152004-02-08 22:55:38 +0000156
Haavard Skinnemoend523e392007-12-13 12:56:28 +0100157static uint flash_offset_cfi[2] = { FLASH_OFFSET_CFI, FLASH_OFFSET_CFI_ALT };
Wolfgang Denkafa0dd02006-12-27 01:26:13 +0100158
Marian Balakowicz513b4a12005-10-11 19:09:42 +0200159/* use CFG_MAX_FLASH_BANKS_DETECT if defined */
160#ifdef CFG_MAX_FLASH_BANKS_DETECT
161static ulong bank_base[CFG_MAX_FLASH_BANKS_DETECT] = CFG_FLASH_BANKS_LIST;
162flash_info_t flash_info[CFG_MAX_FLASH_BANKS_DETECT]; /* FLASH chips info */
163#else
wdenk2cefd152004-02-08 22:55:38 +0000164static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST;
Marian Balakowicz513b4a12005-10-11 19:09:42 +0200165flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* FLASH chips info */
166#endif
wdenk2cefd152004-02-08 22:55:38 +0000167
Stefan Roesec865e6c2006-02-28 15:29:58 +0100168/*
169 * Check if chip width is defined. If not, start detecting with 8bit.
170 */
171#ifndef CFG_FLASH_CFI_WIDTH
172#define CFG_FLASH_CFI_WIDTH FLASH_CFI_8BIT
173#endif
174
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200175typedef unsigned long flash_sect_t;
wdenk2cefd152004-02-08 22:55:38 +0000176
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100177/* CFI standard query structure */
178struct cfi_qry {
179 u8 qry[3];
180 u16 p_id;
181 u16 p_adr;
182 u16 a_id;
183 u16 a_adr;
184 u8 vcc_min;
185 u8 vcc_max;
186 u8 vpp_min;
187 u8 vpp_max;
188 u8 word_write_timeout_typ;
189 u8 buf_write_timeout_typ;
190 u8 block_erase_timeout_typ;
191 u8 chip_erase_timeout_typ;
192 u8 word_write_timeout_max;
193 u8 buf_write_timeout_max;
194 u8 block_erase_timeout_max;
195 u8 chip_erase_timeout_max;
196 u8 dev_size;
197 u16 interface_desc;
198 u16 max_buf_write_size;
199 u8 num_erase_regions;
200 u32 erase_region_info[NUM_ERASE_REGIONS];
201} __attribute__((packed));
202
203struct cfi_pri_hdr {
204 u8 pri[3];
205 u8 major_version;
206 u8 minor_version;
207} __attribute__((packed));
208
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100209static void flash_write8(u8 value, void *addr)
210{
211 __raw_writeb(value, addr);
212}
213
214static void flash_write16(u16 value, void *addr)
215{
216 __raw_writew(value, addr);
217}
218
219static void flash_write32(u32 value, void *addr)
220{
221 __raw_writel(value, addr);
222}
223
224static void flash_write64(u64 value, void *addr)
225{
226 /* No architectures currently implement __raw_writeq() */
227 *(volatile u64 *)addr = value;
228}
229
230static u8 flash_read8(void *addr)
231{
232 return __raw_readb(addr);
233}
234
235static u16 flash_read16(void *addr)
236{
237 return __raw_readw(addr);
238}
239
240static u32 flash_read32(void *addr)
241{
242 return __raw_readl(addr);
243}
244
Daniel Hellstromcfd71382008-03-28 20:40:19 +0100245static u64 __flash_read64(void *addr)
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100246{
247 /* No architectures currently implement __raw_readq() */
248 return *(volatile u64 *)addr;
249}
250
Daniel Hellstromcfd71382008-03-28 20:40:19 +0100251u64 flash_read64(void *addr)__attribute__((weak, alias("__flash_read64")));
252
wdenk2cefd152004-02-08 22:55:38 +0000253/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +0000254 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200255#if defined(CFG_ENV_IS_IN_FLASH) || defined(CFG_ENV_ADDR_REDUND) || (CFG_MONITOR_BASE >= CFG_FLASH_BASE)
256static flash_info_t *flash_get_info(ulong base)
257{
258 int i;
259 flash_info_t * info = 0;
wdenk2cefd152004-02-08 22:55:38 +0000260
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200261 for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
262 info = & flash_info[i];
263 if (info->size && info->start[0] <= base &&
264 base <= info->start[0] + info->size - 1)
265 break;
266 }
wdenk2cefd152004-02-08 22:55:38 +0000267
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200268 return i == CFG_MAX_FLASH_BANKS ? 0 : info;
269}
wdenk2cefd152004-02-08 22:55:38 +0000270#endif
271
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100272unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect)
273{
274 if (sect != (info->sector_count - 1))
275 return info->start[sect + 1] - info->start[sect];
276 else
277 return info->start[0] + info->size - info->start[sect];
278}
279
wdenke65527f2004-02-12 00:47:09 +0000280/*-----------------------------------------------------------------------
281 * create an address based on the offset and the port width
282 */
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100283static inline void *
284flash_map (flash_info_t * info, flash_sect_t sect, uint offset)
wdenke65527f2004-02-12 00:47:09 +0000285{
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100286 unsigned int byte_offset = offset * info->portwidth;
287
288 return map_physmem(info->start[sect] + byte_offset,
289 flash_sector_size(info, sect) - byte_offset,
290 MAP_NOCACHE);
291}
292
293static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
294 unsigned int offset, void *addr)
295{
296 unsigned int byte_offset = offset * info->portwidth;
297
298 unmap_physmem(addr, flash_sector_size(info, sect) - byte_offset);
wdenke65527f2004-02-12 00:47:09 +0000299}
300
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200301/*-----------------------------------------------------------------------
302 * make a proper sized command based on the port and chip widths
303 */
Sebastian Siewior7746ed82008-07-15 13:35:23 +0200304static void flash_make_cmd(flash_info_t *info, u32 cmd, void *cmdbuf)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200305{
306 int i;
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400307 int cword_offset;
308 int cp_offset;
Sebastian Siewiord528cd62008-07-16 20:04:49 +0200309#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
310 u32 cmd_le = cpu_to_le32(cmd);
311#endif
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400312 uchar val;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200313 uchar *cp = (uchar *) cmdbuf;
314
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400315 for (i = info->portwidth; i > 0; i--){
316 cword_offset = (info->portwidth-i)%info->chipwidth;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200317#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400318 cp_offset = info->portwidth - i;
Sebastian Siewiord528cd62008-07-16 20:04:49 +0200319 val = *((uchar*)&cmd_le + cword_offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200320#else
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400321 cp_offset = i - 1;
Sebastian Siewior7746ed82008-07-15 13:35:23 +0200322 val = *((uchar*)&cmd + sizeof(u32) - cword_offset - 1);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200323#endif
Sebastian Siewior7746ed82008-07-15 13:35:23 +0200324 cp[cp_offset] = (cword_offset >= sizeof(u32)) ? 0x00 : val;
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400325 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200326}
327
wdenk2cefd152004-02-08 22:55:38 +0000328#ifdef DEBUG
wdenke65527f2004-02-12 00:47:09 +0000329/*-----------------------------------------------------------------------
330 * Debug support
331 */
Haavard Skinnemoen670a3232007-12-13 12:56:29 +0100332static void print_longlong (char *str, unsigned long long data)
wdenk2cefd152004-02-08 22:55:38 +0000333{
334 int i;
335 char *cp;
wdenke65527f2004-02-12 00:47:09 +0000336
337 cp = (unsigned char *) &data;
338 for (i = 0; i < 8; i++)
339 sprintf (&str[i * 2], "%2.2x", *cp++);
wdenk2cefd152004-02-08 22:55:38 +0000340}
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200341
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100342static void flash_printqry (struct cfi_qry *qry)
wdenke65527f2004-02-12 00:47:09 +0000343{
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100344 u8 *p = (u8 *)qry;
wdenke65527f2004-02-12 00:47:09 +0000345 int x, y;
346
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100347 for (x = 0; x < sizeof(struct cfi_qry); x += 16) {
348 debug("%02x : ", x);
349 for (y = 0; y < 16; y++)
350 debug("%2.2x ", p[x + y]);
351 debug(" ");
wdenke65527f2004-02-12 00:47:09 +0000352 for (y = 0; y < 16; y++) {
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100353 unsigned char c = p[x + y];
354 if (c >= 0x20 && c <= 0x7e)
355 debug("%c", c);
356 else
357 debug(".");
wdenke65527f2004-02-12 00:47:09 +0000358 }
Haavard Skinnemoen53baec72007-12-14 15:36:16 +0100359 debug("\n");
wdenke65527f2004-02-12 00:47:09 +0000360 }
361}
wdenk2cefd152004-02-08 22:55:38 +0000362#endif
363
364
365/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +0000366 * read a character at a port width address
367 */
Haavard Skinnemoen670a3232007-12-13 12:56:29 +0100368static inline uchar flash_read_uchar (flash_info_t * info, uint offset)
wdenk2cefd152004-02-08 22:55:38 +0000369{
370 uchar *cp;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100371 uchar retval;
wdenke65527f2004-02-12 00:47:09 +0000372
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100373 cp = flash_map (info, 0, offset);
Heiko Schocher800db312007-01-19 18:05:26 +0100374#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100375 retval = flash_read8(cp);
wdenke65527f2004-02-12 00:47:09 +0000376#else
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100377 retval = flash_read8(cp + info->portwidth - 1);
wdenke65527f2004-02-12 00:47:09 +0000378#endif
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100379 flash_unmap (info, 0, offset, cp);
380 return retval;
wdenk2cefd152004-02-08 22:55:38 +0000381}
382
383/*-----------------------------------------------------------------------
Tor Krill7f2a3052008-03-28 11:29:10 +0100384 * read a word at a port width address, assume 16bit bus
385 */
386static inline ushort flash_read_word (flash_info_t * info, uint offset)
387{
388 ushort *addr, retval;
389
390 addr = flash_map (info, 0, offset);
391 retval = flash_read16 (addr);
392 flash_unmap (info, 0, offset, addr);
393 return retval;
394}
395
396
397/*-----------------------------------------------------------------------
Stefan Roese12797482006-11-13 13:55:24 +0100398 * read a long word by picking the least significant byte of each maximum
wdenk2cefd152004-02-08 22:55:38 +0000399 * port size word. Swap for ppc format.
400 */
Haavard Skinnemoen670a3232007-12-13 12:56:29 +0100401static ulong flash_read_long (flash_info_t * info, flash_sect_t sect,
402 uint offset)
wdenk2cefd152004-02-08 22:55:38 +0000403{
wdenke65527f2004-02-12 00:47:09 +0000404 uchar *addr;
405 ulong retval;
406
407#ifdef DEBUG
408 int x;
409#endif
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100410 addr = flash_map (info, sect, offset);
wdenk2cefd152004-02-08 22:55:38 +0000411
wdenke65527f2004-02-12 00:47:09 +0000412#ifdef DEBUG
413 debug ("long addr is at %p info->portwidth = %d\n", addr,
414 info->portwidth);
415 for (x = 0; x < 4 * info->portwidth; x++) {
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100416 debug ("addr[%x] = 0x%x\n", x, flash_read8(addr + x));
wdenke65527f2004-02-12 00:47:09 +0000417 }
418#endif
Heiko Schocher800db312007-01-19 18:05:26 +0100419#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100420 retval = ((flash_read8(addr) << 16) |
421 (flash_read8(addr + info->portwidth) << 24) |
422 (flash_read8(addr + 2 * info->portwidth)) |
423 (flash_read8(addr + 3 * info->portwidth) << 8));
wdenke65527f2004-02-12 00:47:09 +0000424#else
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100425 retval = ((flash_read8(addr + 2 * info->portwidth - 1) << 24) |
426 (flash_read8(addr + info->portwidth - 1) << 16) |
427 (flash_read8(addr + 4 * info->portwidth - 1) << 8) |
428 (flash_read8(addr + 3 * info->portwidth - 1)));
wdenke65527f2004-02-12 00:47:09 +0000429#endif
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100430 flash_unmap(info, sect, offset, addr);
431
wdenke65527f2004-02-12 00:47:09 +0000432 return retval;
wdenk2cefd152004-02-08 22:55:38 +0000433}
434
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200435/*
436 * Write a proper sized command to the correct address
Michael Schwingen73d044d2007-12-07 23:35:02 +0100437 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200438static void flash_write_cmd (flash_info_t * info, flash_sect_t sect,
Sebastian Siewior7746ed82008-07-15 13:35:23 +0200439 uint offset, u32 cmd)
Michael Schwingen73d044d2007-12-07 23:35:02 +0100440{
Haavard Skinnemoend523e392007-12-13 12:56:28 +0100441
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100442 void *addr;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200443 cfiword_t cword;
Michael Schwingen73d044d2007-12-07 23:35:02 +0100444
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100445 addr = flash_map (info, sect, offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200446 flash_make_cmd (info, cmd, &cword);
447 switch (info->portwidth) {
448 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100449 debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr, cmd,
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200450 cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100451 flash_write8(cword.c, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200452 break;
453 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100454 debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr,
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200455 cmd, cword.w,
456 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100457 flash_write16(cword.w, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200458 break;
459 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100460 debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit\n", addr,
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200461 cmd, cword.l,
462 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100463 flash_write32(cword.l, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200464 break;
465 case FLASH_CFI_64BIT:
466#ifdef DEBUG
467 {
468 char str[20];
Haavard Skinnemoend523e392007-12-13 12:56:28 +0100469
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200470 print_longlong (str, cword.ll);
471
472 debug ("fwrite addr %p cmd %x %s 64 bit x %d bit\n",
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100473 addr, cmd, str,
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200474 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
Michael Schwingen73d044d2007-12-07 23:35:02 +0100475 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200476#endif
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100477 flash_write64(cword.ll, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200478 break;
Michael Schwingen73d044d2007-12-07 23:35:02 +0100479 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200480
481 /* Ensure all the instructions are fully finished */
482 sync();
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100483
484 flash_unmap(info, sect, offset, addr);
Michael Schwingen73d044d2007-12-07 23:35:02 +0100485}
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200486
487static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect)
Michael Schwingen73d044d2007-12-07 23:35:02 +0100488{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200489 flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_UNLOCK_START);
490 flash_write_cmd (info, sect, info->addr_unlock2, AMD_CMD_UNLOCK_ACK);
Michael Schwingen73d044d2007-12-07 23:35:02 +0100491}
Michael Schwingen73d044d2007-12-07 23:35:02 +0100492
493/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +0000494 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200495static int flash_isequal (flash_info_t * info, flash_sect_t sect,
496 uint offset, uchar cmd)
wdenk2cefd152004-02-08 22:55:38 +0000497{
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100498 void *addr;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200499 cfiword_t cword;
500 int retval;
wdenk2cefd152004-02-08 22:55:38 +0000501
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100502 addr = flash_map (info, sect, offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200503 flash_make_cmd (info, cmd, &cword);
Stefan Roeseefef95b2006-04-01 13:41:03 +0200504
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100505 debug ("is= cmd %x(%c) addr %p ", cmd, cmd, addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200506 switch (info->portwidth) {
507 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100508 debug ("is= %x %x\n", flash_read8(addr), cword.c);
509 retval = (flash_read8(addr) == cword.c);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200510 break;
511 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100512 debug ("is= %4.4x %4.4x\n", flash_read16(addr), cword.w);
513 retval = (flash_read16(addr) == cword.w);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200514 break;
515 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100516 debug ("is= %8.8lx %8.8lx\n", flash_read32(addr), cword.l);
517 retval = (flash_read32(addr) == cword.l);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200518 break;
519 case FLASH_CFI_64BIT:
520#ifdef DEBUG
521 {
522 char str1[20];
523 char str2[20];
Michael Schwingen73d044d2007-12-07 23:35:02 +0100524
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100525 print_longlong (str1, flash_read64(addr));
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200526 print_longlong (str2, cword.ll);
527 debug ("is= %s %s\n", str1, str2);
wdenk2cefd152004-02-08 22:55:38 +0000528 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200529#endif
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100530 retval = (flash_read64(addr) == cword.ll);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200531 break;
532 default:
533 retval = 0;
534 break;
535 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100536 flash_unmap(info, sect, offset, addr);
537
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200538 return retval;
539}
Stefan Roeseefef95b2006-04-01 13:41:03 +0200540
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200541/*-----------------------------------------------------------------------
542 */
543static int flash_isset (flash_info_t * info, flash_sect_t sect,
544 uint offset, uchar cmd)
545{
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100546 void *addr;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200547 cfiword_t cword;
548 int retval;
Stefan Roeseefef95b2006-04-01 13:41:03 +0200549
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100550 addr = flash_map (info, sect, offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200551 flash_make_cmd (info, cmd, &cword);
552 switch (info->portwidth) {
553 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100554 retval = ((flash_read8(addr) & cword.c) == cword.c);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200555 break;
556 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100557 retval = ((flash_read16(addr) & cword.w) == cword.w);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200558 break;
559 case FLASH_CFI_32BIT:
Stefan Roesed4e37c02008-01-02 14:05:37 +0100560 retval = ((flash_read32(addr) & cword.l) == cword.l);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200561 break;
562 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100563 retval = ((flash_read64(addr) & cword.ll) == cword.ll);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200564 break;
565 default:
566 retval = 0;
567 break;
568 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100569 flash_unmap(info, sect, offset, addr);
570
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200571 return retval;
572}
Stefan Roeseefef95b2006-04-01 13:41:03 +0200573
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200574/*-----------------------------------------------------------------------
575 */
576static int flash_toggle (flash_info_t * info, flash_sect_t sect,
577 uint offset, uchar cmd)
578{
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100579 void *addr;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200580 cfiword_t cword;
581 int retval;
wdenke85b7a52004-10-10 22:16:06 +0000582
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100583 addr = flash_map (info, sect, offset);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200584 flash_make_cmd (info, cmd, &cword);
585 switch (info->portwidth) {
586 case FLASH_CFI_8BIT:
Stefan Roesecff2b492008-06-16 10:40:02 +0200587 retval = flash_read8(addr) != flash_read8(addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200588 break;
589 case FLASH_CFI_16BIT:
Stefan Roesecff2b492008-06-16 10:40:02 +0200590 retval = flash_read16(addr) != flash_read16(addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200591 break;
592 case FLASH_CFI_32BIT:
Stefan Roesecff2b492008-06-16 10:40:02 +0200593 retval = flash_read32(addr) != flash_read32(addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200594 break;
595 case FLASH_CFI_64BIT:
Stefan Roesecff2b492008-06-16 10:40:02 +0200596 retval = flash_read64(addr) != flash_read64(addr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200597 break;
598 default:
599 retval = 0;
600 break;
601 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100602 flash_unmap(info, sect, offset, addr);
603
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200604 return retval;
wdenk2cefd152004-02-08 22:55:38 +0000605}
606
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200607/*
608 * flash_is_busy - check to see if the flash is busy
609 *
610 * This routine checks the status of the chip and returns true if the
611 * chip is busy.
wdenk2cefd152004-02-08 22:55:38 +0000612 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200613static int flash_is_busy (flash_info_t * info, flash_sect_t sect)
wdenk5c71a7a2005-05-16 15:23:22 +0000614{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200615 int retval;
wdenk5c71a7a2005-05-16 15:23:22 +0000616
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200617 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400618 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200619 case CFI_CMDSET_INTEL_STANDARD:
620 case CFI_CMDSET_INTEL_EXTENDED:
621 retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE);
622 break;
623 case CFI_CMDSET_AMD_STANDARD:
624 case CFI_CMDSET_AMD_EXTENDED:
625#ifdef CONFIG_FLASH_CFI_LEGACY
626 case CFI_CMDSET_AMD_LEGACY:
627#endif
628 retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE);
629 break;
630 default:
631 retval = 0;
wdenk5c71a7a2005-05-16 15:23:22 +0000632 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200633 debug ("flash_is_busy: %d\n", retval);
634 return retval;
wdenk5c71a7a2005-05-16 15:23:22 +0000635}
636
637/*-----------------------------------------------------------------------
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200638 * wait for XSR.7 to be set. Time out with an error if it does not.
639 * This routine does not set the flash to read-array mode.
wdenk5c71a7a2005-05-16 15:23:22 +0000640 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200641static int flash_status_check (flash_info_t * info, flash_sect_t sector,
642 ulong tout, char *prompt)
wdenk2cefd152004-02-08 22:55:38 +0000643{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200644 ulong start;
wdenk2cefd152004-02-08 22:55:38 +0000645
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200646#if CFG_HZ != 1000
647 tout *= CFG_HZ/1000;
648#endif
wdenk2cefd152004-02-08 22:55:38 +0000649
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200650 /* Wait for command completion */
651 start = get_timer (0);
652 while (flash_is_busy (info, sector)) {
653 if (get_timer (start) > tout) {
654 printf ("Flash %s timeout at address %lx data %lx\n",
655 prompt, info->start[sector],
656 flash_read_long (info, sector, 0));
657 flash_write_cmd (info, sector, 0, info->cmd_reset);
658 return ERR_TIMOUT;
wdenk2cefd152004-02-08 22:55:38 +0000659 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200660 udelay (1); /* also triggers watchdog */
wdenk2cefd152004-02-08 22:55:38 +0000661 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200662 return ERR_OK;
663}
wdenk2cefd152004-02-08 22:55:38 +0000664
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200665/*-----------------------------------------------------------------------
666 * Wait for XSR.7 to be set, if it times out print an error, otherwise
667 * do a full status check.
668 *
669 * This routine sets the flash to read-array mode.
670 */
671static int flash_full_status_check (flash_info_t * info, flash_sect_t sector,
672 ulong tout, char *prompt)
673{
674 int retcode;
wdenk2cefd152004-02-08 22:55:38 +0000675
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200676 retcode = flash_status_check (info, sector, tout, prompt);
677 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400678 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200679 case CFI_CMDSET_INTEL_EXTENDED:
680 case CFI_CMDSET_INTEL_STANDARD:
681 if ((retcode == ERR_OK)
682 && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) {
683 retcode = ERR_INVAL;
684 printf ("Flash %s error at address %lx\n", prompt,
685 info->start[sector]);
686 if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS |
687 FLASH_STATUS_PSLBS)) {
688 puts ("Command Sequence Error.\n");
689 } else if (flash_isset (info, sector, 0,
690 FLASH_STATUS_ECLBS)) {
691 puts ("Block Erase Error.\n");
692 retcode = ERR_NOT_ERASED;
693 } else if (flash_isset (info, sector, 0,
694 FLASH_STATUS_PSLBS)) {
695 puts ("Locking Error\n");
wdenk2cefd152004-02-08 22:55:38 +0000696 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200697 if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) {
698 puts ("Block locked.\n");
699 retcode = ERR_PROTECTED;
700 }
701 if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS))
702 puts ("Vpp Low Error.\n");
wdenk2cefd152004-02-08 22:55:38 +0000703 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200704 flash_write_cmd (info, sector, 0, info->cmd_reset);
705 break;
706 default:
707 break;
wdenk2cefd152004-02-08 22:55:38 +0000708 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200709 return retcode;
wdenk2cefd152004-02-08 22:55:38 +0000710}
711
712/*-----------------------------------------------------------------------
713 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200714static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
wdenk2cefd152004-02-08 22:55:38 +0000715{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200716#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
717 unsigned short w;
718 unsigned int l;
719 unsigned long long ll;
720#endif
wdenk2cefd152004-02-08 22:55:38 +0000721
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200722 switch (info->portwidth) {
723 case FLASH_CFI_8BIT:
724 cword->c = c;
725 break;
726 case FLASH_CFI_16BIT:
727#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
728 w = c;
729 w <<= 8;
730 cword->w = (cword->w >> 8) | w;
731#else
732 cword->w = (cword->w << 8) | c;
Michael Schwingen73d044d2007-12-07 23:35:02 +0100733#endif
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200734 break;
735 case FLASH_CFI_32BIT:
736#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
737 l = c;
738 l <<= 24;
739 cword->l = (cword->l >> 8) | l;
740#else
741 cword->l = (cword->l << 8) | c;
742#endif
743 break;
744 case FLASH_CFI_64BIT:
745#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
746 ll = c;
747 ll <<= 56;
748 cword->ll = (cword->ll >> 8) | ll;
749#else
750 cword->ll = (cword->ll << 8) | c;
751#endif
752 break;
Stefan Roese12797482006-11-13 13:55:24 +0100753 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200754}
wdenk2cefd152004-02-08 22:55:38 +0000755
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200756/* loop through the sectors from the highest address when the passed
757 * address is greater or equal to the sector address we have a match
758 */
759static flash_sect_t find_sector (flash_info_t * info, ulong addr)
760{
761 flash_sect_t sector;
wdenk2cefd152004-02-08 22:55:38 +0000762
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200763 for (sector = info->sector_count - 1; sector >= 0; sector--) {
764 if (addr >= info->start[sector])
765 break;
wdenk2cefd152004-02-08 22:55:38 +0000766 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200767 return sector;
wdenk2cefd152004-02-08 22:55:38 +0000768}
769
770/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +0000771 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200772static int flash_write_cfiword (flash_info_t * info, ulong dest,
773 cfiword_t cword)
wdenk2cefd152004-02-08 22:55:38 +0000774{
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100775 void *dstaddr;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200776 int flag;
wdenk2cefd152004-02-08 22:55:38 +0000777
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100778 dstaddr = map_physmem(dest, info->portwidth, MAP_NOCACHE);
wdenk2cefd152004-02-08 22:55:38 +0000779
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200780 /* Check if Flash is (sufficiently) erased */
781 switch (info->portwidth) {
782 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100783 flag = ((flash_read8(dstaddr) & cword.c) == cword.c);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200784 break;
785 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100786 flag = ((flash_read16(dstaddr) & cword.w) == cword.w);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200787 break;
788 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100789 flag = ((flash_read32(dstaddr) & cword.l) == cword.l);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200790 break;
791 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100792 flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200793 break;
794 default:
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100795 flag = 0;
796 break;
wdenk2cefd152004-02-08 22:55:38 +0000797 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100798 if (!flag) {
799 unmap_physmem(dstaddr, info->portwidth);
Stefan Roese707c1462007-12-27 07:50:54 +0100800 return ERR_NOT_ERASED;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100801 }
wdenk2cefd152004-02-08 22:55:38 +0000802
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200803 /* Disable interrupts which might cause a timeout here */
804 flag = disable_interrupts ();
Stefan Roesec865e6c2006-02-28 15:29:58 +0100805
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200806 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400807 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200808 case CFI_CMDSET_INTEL_EXTENDED:
809 case CFI_CMDSET_INTEL_STANDARD:
810 flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS);
811 flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE);
812 break;
813 case CFI_CMDSET_AMD_EXTENDED:
814 case CFI_CMDSET_AMD_STANDARD:
815#ifdef CONFIG_FLASH_CFI_LEGACY
816 case CFI_CMDSET_AMD_LEGACY:
817#endif
818 flash_unlock_seq (info, 0);
819 flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_WRITE);
820 break;
wdenk2cefd152004-02-08 22:55:38 +0000821 }
822
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200823 switch (info->portwidth) {
824 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100825 flash_write8(cword.c, dstaddr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200826 break;
827 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100828 flash_write16(cword.w, dstaddr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200829 break;
830 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100831 flash_write32(cword.l, dstaddr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200832 break;
833 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100834 flash_write64(cword.ll, dstaddr);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200835 break;
wdenk2cefd152004-02-08 22:55:38 +0000836 }
837
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200838 /* re-enable interrupts if necessary */
839 if (flag)
840 enable_interrupts ();
wdenk2cefd152004-02-08 22:55:38 +0000841
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100842 unmap_physmem(dstaddr, info->portwidth);
843
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200844 return flash_full_status_check (info, find_sector (info, dest),
845 info->write_tout, "write");
wdenk2cefd152004-02-08 22:55:38 +0000846}
847
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200848#ifdef CFG_FLASH_USE_BUFFER_WRITE
wdenk2cefd152004-02-08 22:55:38 +0000849
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200850static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp,
851 int len)
wdenk2cefd152004-02-08 22:55:38 +0000852{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200853 flash_sect_t sector;
854 int cnt;
855 int retcode;
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100856 void *src = cp;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100857 void *dst = map_physmem(dest, len, MAP_NOCACHE);
Stefan Roese707c1462007-12-27 07:50:54 +0100858 void *dst2 = dst;
859 int flag = 0;
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200860 uint offset = 0;
861 unsigned int shift;
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400862 uchar write_cmd;
Stefan Roese707c1462007-12-27 07:50:54 +0100863
864 switch (info->portwidth) {
865 case FLASH_CFI_8BIT:
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200866 shift = 0;
Stefan Roese707c1462007-12-27 07:50:54 +0100867 break;
868 case FLASH_CFI_16BIT:
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200869 shift = 1;
Stefan Roese707c1462007-12-27 07:50:54 +0100870 break;
871 case FLASH_CFI_32BIT:
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200872 shift = 2;
Stefan Roese707c1462007-12-27 07:50:54 +0100873 break;
874 case FLASH_CFI_64BIT:
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200875 shift = 3;
Stefan Roese707c1462007-12-27 07:50:54 +0100876 break;
877 default:
878 retcode = ERR_INVAL;
879 goto out_unmap;
880 }
881
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200882 cnt = len >> shift;
883
Stefan Roese707c1462007-12-27 07:50:54 +0100884 while ((cnt-- > 0) && (flag == 0)) {
885 switch (info->portwidth) {
886 case FLASH_CFI_8BIT:
887 flag = ((flash_read8(dst2) & flash_read8(src)) ==
888 flash_read8(src));
889 src += 1, dst2 += 1;
890 break;
891 case FLASH_CFI_16BIT:
892 flag = ((flash_read16(dst2) & flash_read16(src)) ==
893 flash_read16(src));
894 src += 2, dst2 += 2;
895 break;
896 case FLASH_CFI_32BIT:
897 flag = ((flash_read32(dst2) & flash_read32(src)) ==
898 flash_read32(src));
899 src += 4, dst2 += 4;
900 break;
901 case FLASH_CFI_64BIT:
902 flag = ((flash_read64(dst2) & flash_read64(src)) ==
903 flash_read64(src));
904 src += 8, dst2 += 8;
905 break;
906 }
907 }
908 if (!flag) {
909 retcode = ERR_NOT_ERASED;
910 goto out_unmap;
911 }
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100912
Stefan Roese707c1462007-12-27 07:50:54 +0100913 src = cp;
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100914 sector = find_sector (info, dest);
wdenke65527f2004-02-12 00:47:09 +0000915
916 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400917 case CFI_CMDSET_INTEL_PROG_REGIONS:
wdenk2cefd152004-02-08 22:55:38 +0000918 case CFI_CMDSET_INTEL_STANDARD:
919 case CFI_CMDSET_INTEL_EXTENDED:
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400920 write_cmd = (info->vendor == CFI_CMDSET_INTEL_PROG_REGIONS) ?
921 FLASH_CMD_WRITE_BUFFER_PROG : FLASH_CMD_WRITE_TO_BUFFER;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200922 flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS);
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +0400923 flash_write_cmd (info, sector, 0, FLASH_CMD_READ_STATUS);
924 flash_write_cmd (info, sector, 0, write_cmd);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200925 retcode = flash_status_check (info, sector,
926 info->buffer_write_tout,
927 "write to buffer");
928 if (retcode == ERR_OK) {
929 /* reduce the number of loops by the width of
930 * the port */
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200931 cnt = len >> shift;
Vasiliy Leoenenkoc47c0d42008-05-07 21:24:44 +0400932 flash_write_cmd (info, sector, 0, cnt - 1);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200933 while (cnt-- > 0) {
934 switch (info->portwidth) {
935 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100936 flash_write8(flash_read8(src), dst);
937 src += 1, dst += 1;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200938 break;
939 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100940 flash_write16(flash_read16(src), dst);
941 src += 2, dst += 2;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200942 break;
943 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100944 flash_write32(flash_read32(src), dst);
945 src += 4, dst += 4;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200946 break;
947 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100948 flash_write64(flash_read64(src), dst);
949 src += 8, dst += 8;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200950 break;
951 default:
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100952 retcode = ERR_INVAL;
953 goto out_unmap;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200954 }
955 }
956 flash_write_cmd (info, sector, 0,
957 FLASH_CMD_WRITE_BUFFER_CONFIRM);
958 retcode = flash_full_status_check (
959 info, sector, info->buffer_write_tout,
960 "buffer write");
961 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +0100962
963 break;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200964
wdenk2cefd152004-02-08 22:55:38 +0000965 case CFI_CMDSET_AMD_STANDARD:
966 case CFI_CMDSET_AMD_EXTENDED:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200967 flash_unlock_seq(info,0);
Guennadi Liakhovetski183284f2008-04-03 13:36:02 +0200968
969#ifdef CONFIG_FLASH_SPANSION_S29WS_N
970 offset = ((unsigned long)dst - info->start[sector]) >> shift;
971#endif
972 flash_write_cmd(info, sector, offset, AMD_CMD_WRITE_TO_BUFFER);
973 cnt = len >> shift;
974 flash_write_cmd(info, sector, offset, (uchar)cnt - 1);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200975
976 switch (info->portwidth) {
977 case FLASH_CFI_8BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100978 while (cnt-- > 0) {
979 flash_write8(flash_read8(src), dst);
980 src += 1, dst += 1;
981 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200982 break;
983 case FLASH_CFI_16BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100984 while (cnt-- > 0) {
985 flash_write16(flash_read16(src), dst);
986 src += 2, dst += 2;
987 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200988 break;
989 case FLASH_CFI_32BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100990 while (cnt-- > 0) {
991 flash_write32(flash_read32(src), dst);
992 src += 4, dst += 4;
993 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +0200994 break;
995 case FLASH_CFI_64BIT:
Haavard Skinnemoen21b95b42007-12-13 12:56:32 +0100996 while (cnt-- > 0) {
997 flash_write64(flash_read64(src), dst);
998 src += 8, dst += 8;
999 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001000 break;
1001 default:
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001002 retcode = ERR_INVAL;
1003 goto out_unmap;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001004 }
1005
1006 flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM);
1007 retcode = flash_full_status_check (info, sector,
1008 info->buffer_write_tout,
1009 "buffer write");
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001010 break;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001011
wdenk2cefd152004-02-08 22:55:38 +00001012 default:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001013 debug ("Unknown Command Set\n");
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001014 retcode = ERR_INVAL;
1015 break;
wdenk2cefd152004-02-08 22:55:38 +00001016 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001017
1018out_unmap:
1019 unmap_physmem(dst, len);
1020 return retcode;
wdenk2cefd152004-02-08 22:55:38 +00001021}
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001022#endif /* CFG_FLASH_USE_BUFFER_WRITE */
1023
wdenke65527f2004-02-12 00:47:09 +00001024
wdenk2cefd152004-02-08 22:55:38 +00001025/*-----------------------------------------------------------------------
wdenk2cefd152004-02-08 22:55:38 +00001026 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001027int flash_erase (flash_info_t * info, int s_first, int s_last)
wdenk2cefd152004-02-08 22:55:38 +00001028{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001029 int rcode = 0;
1030 int prot;
1031 flash_sect_t sect;
wdenk2cefd152004-02-08 22:55:38 +00001032
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001033 if (info->flash_id != FLASH_MAN_CFI) {
1034 puts ("Can't erase unknown flash type - aborted\n");
1035 return 1;
1036 }
1037 if ((s_first < 0) || (s_first > s_last)) {
1038 puts ("- no sectors to erase\n");
1039 return 1;
1040 }
Stefan Roeseefef95b2006-04-01 13:41:03 +02001041
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001042 prot = 0;
1043 for (sect = s_first; sect <= s_last; ++sect) {
1044 if (info->protect[sect]) {
1045 prot++;
wdenk2cefd152004-02-08 22:55:38 +00001046 }
1047 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001048 if (prot) {
1049 printf ("- Warning: %d protected sectors will not be erased!\n",
1050 prot);
1051 } else {
1052 putc ('\n');
1053 }
wdenke65527f2004-02-12 00:47:09 +00001054
wdenke65527f2004-02-12 00:47:09 +00001055
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001056 for (sect = s_first; sect <= s_last; sect++) {
1057 if (info->protect[sect] == 0) { /* not protected */
1058 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001059 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001060 case CFI_CMDSET_INTEL_STANDARD:
1061 case CFI_CMDSET_INTEL_EXTENDED:
1062 flash_write_cmd (info, sect, 0,
1063 FLASH_CMD_CLEAR_STATUS);
1064 flash_write_cmd (info, sect, 0,
1065 FLASH_CMD_BLOCK_ERASE);
1066 flash_write_cmd (info, sect, 0,
1067 FLASH_CMD_ERASE_CONFIRM);
1068 break;
1069 case CFI_CMDSET_AMD_STANDARD:
1070 case CFI_CMDSET_AMD_EXTENDED:
1071 flash_unlock_seq (info, sect);
1072 flash_write_cmd (info, sect,
1073 info->addr_unlock1,
1074 AMD_CMD_ERASE_START);
1075 flash_unlock_seq (info, sect);
1076 flash_write_cmd (info, sect, 0,
1077 AMD_CMD_ERASE_SECTOR);
1078 break;
1079#ifdef CONFIG_FLASH_CFI_LEGACY
1080 case CFI_CMDSET_AMD_LEGACY:
1081 flash_unlock_seq (info, 0);
1082 flash_write_cmd (info, 0, info->addr_unlock1,
1083 AMD_CMD_ERASE_START);
1084 flash_unlock_seq (info, 0);
1085 flash_write_cmd (info, sect, 0,
1086 AMD_CMD_ERASE_SECTOR);
1087 break;
1088#endif
1089 default:
1090 debug ("Unkown flash vendor %d\n",
1091 info->vendor);
1092 break;
wdenke65527f2004-02-12 00:47:09 +00001093 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001094
1095 if (flash_full_status_check
1096 (info, sect, info->erase_blk_tout, "erase")) {
1097 rcode = 1;
1098 } else
1099 putc ('.');
wdenk2cefd152004-02-08 22:55:38 +00001100 }
wdenk2cefd152004-02-08 22:55:38 +00001101 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001102 puts (" done\n");
1103 return rcode;
wdenk2cefd152004-02-08 22:55:38 +00001104}
wdenke65527f2004-02-12 00:47:09 +00001105
wdenk2cefd152004-02-08 22:55:38 +00001106/*-----------------------------------------------------------------------
1107 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001108void flash_print_info (flash_info_t * info)
wdenk2cefd152004-02-08 22:55:38 +00001109{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001110 int i;
wdenk369d43d2004-03-14 14:09:05 +00001111
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001112 if (info->flash_id != FLASH_MAN_CFI) {
1113 puts ("missing or unknown FLASH type\n");
1114 return;
1115 }
1116
1117 printf ("%s FLASH (%d x %d)",
1118 info->name,
1119 (info->portwidth << 3), (info->chipwidth << 3));
1120 if (info->size < 1024*1024)
1121 printf (" Size: %ld kB in %d Sectors\n",
1122 info->size >> 10, info->sector_count);
1123 else
1124 printf (" Size: %ld MB in %d Sectors\n",
1125 info->size >> 20, info->sector_count);
1126 printf (" ");
1127 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001128 case CFI_CMDSET_INTEL_PROG_REGIONS:
1129 printf ("Intel Prog Regions");
1130 break;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001131 case CFI_CMDSET_INTEL_STANDARD:
1132 printf ("Intel Standard");
1133 break;
1134 case CFI_CMDSET_INTEL_EXTENDED:
1135 printf ("Intel Extended");
1136 break;
1137 case CFI_CMDSET_AMD_STANDARD:
1138 printf ("AMD Standard");
1139 break;
1140 case CFI_CMDSET_AMD_EXTENDED:
1141 printf ("AMD Extended");
1142 break;
1143#ifdef CONFIG_FLASH_CFI_LEGACY
1144 case CFI_CMDSET_AMD_LEGACY:
1145 printf ("AMD Legacy");
1146 break;
wdenk369d43d2004-03-14 14:09:05 +00001147#endif
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001148 default:
1149 printf ("Unknown (%d)", info->vendor);
1150 break;
wdenk2cefd152004-02-08 22:55:38 +00001151 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001152 printf (" command set, Manufacturer ID: 0x%02X, Device ID: 0x%02X",
1153 info->manufacturer_id, info->device_id);
1154 if (info->device_id == 0x7E) {
1155 printf("%04X", info->device_id2);
1156 }
1157 printf ("\n Erase timeout: %ld ms, write timeout: %ld ms\n",
1158 info->erase_blk_tout,
1159 info->write_tout);
1160 if (info->buffer_size > 1) {
1161 printf (" Buffer write timeout: %ld ms, "
1162 "buffer size: %d bytes\n",
1163 info->buffer_write_tout,
1164 info->buffer_size);
1165 }
wdenk2cefd152004-02-08 22:55:38 +00001166
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001167 puts ("\n Sector Start Addresses:");
1168 for (i = 0; i < info->sector_count; ++i) {
1169 if ((i % 5) == 0)
1170 printf ("\n");
1171#ifdef CFG_FLASH_EMPTY_INFO
1172 int k;
1173 int size;
1174 int erased;
1175 volatile unsigned long *flash;
wdenk2cefd152004-02-08 22:55:38 +00001176
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001177 /*
1178 * Check if whole sector is erased
1179 */
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001180 size = flash_sector_size(info, i);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001181 erased = 1;
1182 flash = (volatile unsigned long *) info->start[i];
1183 size = size >> 2; /* divide by 4 for longword access */
1184 for (k = 0; k < size; k++) {
1185 if (*flash++ != 0xffffffff) {
1186 erased = 0;
1187 break;
1188 }
1189 }
wdenke65527f2004-02-12 00:47:09 +00001190
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001191 /* print empty and read-only info */
1192 printf (" %08lX %c %s ",
1193 info->start[i],
1194 erased ? 'E' : ' ',
1195 info->protect[i] ? "RO" : " ");
1196#else /* ! CFG_FLASH_EMPTY_INFO */
1197 printf (" %08lX %s ",
1198 info->start[i],
1199 info->protect[i] ? "RO" : " ");
wdenke65527f2004-02-12 00:47:09 +00001200#endif
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001201 }
1202 putc ('\n');
1203 return;
wdenk2cefd152004-02-08 22:55:38 +00001204}
1205
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001206/*-----------------------------------------------------------------------
Jerry Van Barenaae73572008-03-08 13:48:01 -05001207 * This is used in a few places in write_buf() to show programming
1208 * progress. Making it a function is nasty because it needs to do side
1209 * effect updates to digit and dots. Repeated code is nasty too, so
1210 * we define it once here.
1211 */
Stefan Roese7758c162008-03-19 07:09:26 +01001212#ifdef CONFIG_FLASH_SHOW_PROGRESS
1213#define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub) \
1214 dots -= dots_sub; \
Jerry Van Barenaae73572008-03-08 13:48:01 -05001215 if ((scale > 0) && (dots <= 0)) { \
1216 if ((digit % 5) == 0) \
1217 printf ("%d", digit / 5); \
1218 else \
1219 putc ('.'); \
1220 digit--; \
1221 dots += scale; \
1222 }
Stefan Roese7758c162008-03-19 07:09:26 +01001223#else
1224#define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub)
1225#endif
Jerry Van Barenaae73572008-03-08 13:48:01 -05001226
1227/*-----------------------------------------------------------------------
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001228 * Copy memory to flash, returns:
1229 * 0 - OK
1230 * 1 - write timeout
1231 * 2 - Flash not erased
wdenk2cefd152004-02-08 22:55:38 +00001232 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001233int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
wdenk2cefd152004-02-08 22:55:38 +00001234{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001235 ulong wp;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001236 uchar *p;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001237 int aln;
wdenk2cefd152004-02-08 22:55:38 +00001238 cfiword_t cword;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001239 int i, rc;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001240#ifdef CFG_FLASH_USE_BUFFER_WRITE
1241 int buffered_size;
wdenk2cefd152004-02-08 22:55:38 +00001242#endif
Jerry Van Barenaae73572008-03-08 13:48:01 -05001243#ifdef CONFIG_FLASH_SHOW_PROGRESS
1244 int digit = CONFIG_FLASH_SHOW_PROGRESS;
1245 int scale = 0;
1246 int dots = 0;
1247
1248 /*
1249 * Suppress if there are fewer than CONFIG_FLASH_SHOW_PROGRESS writes.
1250 */
1251 if (cnt >= CONFIG_FLASH_SHOW_PROGRESS) {
1252 scale = (int)((cnt + CONFIG_FLASH_SHOW_PROGRESS - 1) /
1253 CONFIG_FLASH_SHOW_PROGRESS);
1254 }
1255#endif
1256
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001257 /* get lower aligned address */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001258 wp = (addr & ~(info->portwidth - 1));
Haiying Wangc123a382007-02-21 16:52:31 +01001259
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001260 /* handle unaligned start */
1261 if ((aln = addr - wp) != 0) {
1262 cword.l = 0;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001263 p = map_physmem(wp, info->portwidth, MAP_NOCACHE);
1264 for (i = 0; i < aln; ++i)
1265 flash_add_byte (info, &cword, flash_read8(p + i));
wdenk2cefd152004-02-08 22:55:38 +00001266
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001267 for (; (i < info->portwidth) && (cnt > 0); i++) {
1268 flash_add_byte (info, &cword, *src++);
1269 cnt--;
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001270 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001271 for (; (cnt == 0) && (i < info->portwidth); ++i)
1272 flash_add_byte (info, &cword, flash_read8(p + i));
1273
1274 rc = flash_write_cfiword (info, wp, cword);
1275 unmap_physmem(p, info->portwidth);
1276 if (rc != 0)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001277 return rc;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001278
1279 wp += i;
Stefan Roese7758c162008-03-19 07:09:26 +01001280 FLASH_SHOW_PROGRESS(scale, dots, digit, i);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001281 }
1282
1283 /* handle the aligned part */
1284#ifdef CFG_FLASH_USE_BUFFER_WRITE
1285 buffered_size = (info->portwidth / info->chipwidth);
1286 buffered_size *= info->buffer_size;
1287 while (cnt >= info->portwidth) {
1288 /* prohibit buffer write when buffer_size is 1 */
1289 if (info->buffer_size == 1) {
1290 cword.l = 0;
1291 for (i = 0; i < info->portwidth; i++)
1292 flash_add_byte (info, &cword, *src++);
1293 if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
1294 return rc;
1295 wp += info->portwidth;
1296 cnt -= info->portwidth;
1297 continue;
1298 }
1299
1300 /* write buffer until next buffered_size aligned boundary */
1301 i = buffered_size - (wp % buffered_size);
1302 if (i > cnt)
1303 i = cnt;
1304 if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK)
1305 return rc;
1306 i -= i & (info->portwidth - 1);
1307 wp += i;
1308 src += i;
1309 cnt -= i;
Stefan Roese7758c162008-03-19 07:09:26 +01001310 FLASH_SHOW_PROGRESS(scale, dots, digit, i);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001311 }
1312#else
1313 while (cnt >= info->portwidth) {
1314 cword.l = 0;
1315 for (i = 0; i < info->portwidth; i++) {
1316 flash_add_byte (info, &cword, *src++);
1317 }
1318 if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
1319 return rc;
1320 wp += info->portwidth;
1321 cnt -= info->portwidth;
Stefan Roese7758c162008-03-19 07:09:26 +01001322 FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001323 }
1324#endif /* CFG_FLASH_USE_BUFFER_WRITE */
Jerry Van Barenaae73572008-03-08 13:48:01 -05001325
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001326 if (cnt == 0) {
1327 return (0);
1328 }
1329
1330 /*
1331 * handle unaligned tail bytes
1332 */
1333 cword.l = 0;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001334 p = map_physmem(wp, info->portwidth, MAP_NOCACHE);
1335 for (i = 0; (i < info->portwidth) && (cnt > 0); ++i) {
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001336 flash_add_byte (info, &cword, *src++);
1337 --cnt;
1338 }
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001339 for (; i < info->portwidth; ++i)
1340 flash_add_byte (info, &cword, flash_read8(p + i));
1341 unmap_physmem(p, info->portwidth);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001342
1343 return flash_write_cfiword (info, wp, cword);
wdenk2cefd152004-02-08 22:55:38 +00001344}
wdenke65527f2004-02-12 00:47:09 +00001345
wdenk2cefd152004-02-08 22:55:38 +00001346/*-----------------------------------------------------------------------
1347 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001348#ifdef CFG_FLASH_PROTECTION
1349
1350int flash_real_protect (flash_info_t * info, long sector, int prot)
wdenk2cefd152004-02-08 22:55:38 +00001351{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001352 int retcode = 0;
wdenke65527f2004-02-12 00:47:09 +00001353
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001354 flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS);
1355 flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT);
1356 if (prot)
1357 flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_SET);
1358 else
1359 flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_CLEAR);
wdenk2cefd152004-02-08 22:55:38 +00001360
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001361 if ((retcode =
1362 flash_full_status_check (info, sector, info->erase_blk_tout,
1363 prot ? "protect" : "unprotect")) == 0) {
wdenke65527f2004-02-12 00:47:09 +00001364
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001365 info->protect[sector] = prot;
1366
1367 /*
1368 * On some of Intel's flash chips (marked via legacy_unlock)
1369 * unprotect unprotects all locking.
1370 */
1371 if ((prot == 0) && (info->legacy_unlock)) {
1372 flash_sect_t i;
1373
1374 for (i = 0; i < info->sector_count; i++) {
1375 if (info->protect[i])
1376 flash_real_protect (info, i, 1);
1377 }
wdenk2cefd152004-02-08 22:55:38 +00001378 }
wdenk2cefd152004-02-08 22:55:38 +00001379 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001380 return retcode;
wdenk2cefd152004-02-08 22:55:38 +00001381}
wdenke65527f2004-02-12 00:47:09 +00001382
wdenk2cefd152004-02-08 22:55:38 +00001383/*-----------------------------------------------------------------------
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001384 * flash_read_user_serial - read the OneTimeProgramming cells
wdenk2cefd152004-02-08 22:55:38 +00001385 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001386void flash_read_user_serial (flash_info_t * info, void *buffer, int offset,
1387 int len)
wdenk2cefd152004-02-08 22:55:38 +00001388{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001389 uchar *src;
1390 uchar *dst;
wdenke65527f2004-02-12 00:47:09 +00001391
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001392 dst = buffer;
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001393 src = flash_map (info, 0, FLASH_OFFSET_USER_PROTECTION);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001394 flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
1395 memcpy (dst, src + offset, len);
1396 flash_write_cmd (info, 0, 0, info->cmd_reset);
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001397 flash_unmap(info, 0, FLASH_OFFSET_USER_PROTECTION, src);
wdenk2cefd152004-02-08 22:55:38 +00001398}
1399
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001400/*
1401 * flash_read_factory_serial - read the device Id from the protection area
wdenk2cefd152004-02-08 22:55:38 +00001402 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001403void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset,
1404 int len)
wdenk2cefd152004-02-08 22:55:38 +00001405{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001406 uchar *src;
wdenke65527f2004-02-12 00:47:09 +00001407
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001408 src = flash_map (info, 0, FLASH_OFFSET_INTEL_PROTECTION);
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001409 flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
1410 memcpy (buffer, src + offset, len);
1411 flash_write_cmd (info, 0, 0, info->cmd_reset);
Haavard Skinnemoene3ea1882007-12-13 12:56:34 +01001412 flash_unmap(info, 0, FLASH_OFFSET_INTEL_PROTECTION, src);
wdenk2cefd152004-02-08 22:55:38 +00001413}
1414
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001415#endif /* CFG_FLASH_PROTECTION */
1416
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001417/*-----------------------------------------------------------------------
1418 * Reverse the order of the erase regions in the CFI QRY structure.
1419 * This is needed for chips that are either a) correctly detected as
1420 * top-boot, or b) buggy.
1421 */
1422static void cfi_reverse_geometry(struct cfi_qry *qry)
1423{
1424 unsigned int i, j;
1425 u32 tmp;
1426
1427 for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) {
1428 tmp = qry->erase_region_info[i];
1429 qry->erase_region_info[i] = qry->erase_region_info[j];
1430 qry->erase_region_info[j] = tmp;
1431 }
1432}
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001433
wdenk2cefd152004-02-08 22:55:38 +00001434/*-----------------------------------------------------------------------
Stefan Roese12797482006-11-13 13:55:24 +01001435 * read jedec ids from device and set corresponding fields in info struct
1436 *
1437 * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct
1438 *
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001439 */
1440static void cmdset_intel_read_jedec_ids(flash_info_t *info)
1441{
1442 flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
1443 flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID);
1444 udelay(1000); /* some flash are slow to respond */
1445 info->manufacturer_id = flash_read_uchar (info,
1446 FLASH_OFFSET_MANUFACTURER_ID);
1447 info->device_id = flash_read_uchar (info,
1448 FLASH_OFFSET_DEVICE_ID);
1449 flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
1450}
1451
1452static int cmdset_intel_init(flash_info_t *info, struct cfi_qry *qry)
1453{
1454 info->cmd_reset = FLASH_CMD_RESET;
1455
1456 cmdset_intel_read_jedec_ids(info);
1457 flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI);
1458
1459#ifdef CFG_FLASH_PROTECTION
1460 /* read legacy lock/unlock bit from intel flash */
1461 if (info->ext_addr) {
1462 info->legacy_unlock = flash_read_uchar (info,
1463 info->ext_addr + 5) & 0x08;
1464 }
1465#endif
1466
1467 return 0;
1468}
1469
1470static void cmdset_amd_read_jedec_ids(flash_info_t *info)
1471{
1472 flash_write_cmd(info, 0, 0, AMD_CMD_RESET);
1473 flash_unlock_seq(info, 0);
1474 flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID);
1475 udelay(1000); /* some flash are slow to respond */
Tor Krill7f2a3052008-03-28 11:29:10 +01001476
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001477 info->manufacturer_id = flash_read_uchar (info,
1478 FLASH_OFFSET_MANUFACTURER_ID);
Tor Krill7f2a3052008-03-28 11:29:10 +01001479
1480 switch (info->chipwidth){
1481 case FLASH_CFI_8BIT:
1482 info->device_id = flash_read_uchar (info,
1483 FLASH_OFFSET_DEVICE_ID);
1484 if (info->device_id == 0x7E) {
1485 /* AMD 3-byte (expanded) device ids */
1486 info->device_id2 = flash_read_uchar (info,
1487 FLASH_OFFSET_DEVICE_ID2);
1488 info->device_id2 <<= 8;
1489 info->device_id2 |= flash_read_uchar (info,
1490 FLASH_OFFSET_DEVICE_ID3);
1491 }
1492 break;
1493 case FLASH_CFI_16BIT:
1494 info->device_id = flash_read_word (info,
1495 FLASH_OFFSET_DEVICE_ID);
1496 break;
1497 default:
1498 break;
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001499 }
1500 flash_write_cmd(info, 0, 0, AMD_CMD_RESET);
1501}
1502
1503static int cmdset_amd_init(flash_info_t *info, struct cfi_qry *qry)
1504{
1505 info->cmd_reset = AMD_CMD_RESET;
1506
1507 cmdset_amd_read_jedec_ids(info);
1508 flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI);
1509
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001510 return 0;
1511}
1512
1513#ifdef CONFIG_FLASH_CFI_LEGACY
Stefan Roese12797482006-11-13 13:55:24 +01001514static void flash_read_jedec_ids (flash_info_t * info)
1515{
1516 info->manufacturer_id = 0;
1517 info->device_id = 0;
1518 info->device_id2 = 0;
1519
1520 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001521 case CFI_CMDSET_INTEL_PROG_REGIONS:
Stefan Roese12797482006-11-13 13:55:24 +01001522 case CFI_CMDSET_INTEL_STANDARD:
1523 case CFI_CMDSET_INTEL_EXTENDED:
Michael Schwingen5fb0aa42008-01-12 20:29:47 +01001524 cmdset_intel_read_jedec_ids(info);
Stefan Roese12797482006-11-13 13:55:24 +01001525 break;
1526 case CFI_CMDSET_AMD_STANDARD:
1527 case CFI_CMDSET_AMD_EXTENDED:
Michael Schwingen5fb0aa42008-01-12 20:29:47 +01001528 cmdset_amd_read_jedec_ids(info);
Stefan Roese12797482006-11-13 13:55:24 +01001529 break;
1530 default:
1531 break;
1532 }
1533}
1534
1535/*-----------------------------------------------------------------------
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001536 * Call board code to request info about non-CFI flash.
1537 * board_flash_get_legacy needs to fill in at least:
1538 * info->portwidth, info->chipwidth and info->interface for Jedec probing.
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001539 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001540static int flash_detect_legacy(ulong base, int banknum)
wdenk2cefd152004-02-08 22:55:38 +00001541{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001542 flash_info_t *info = &flash_info[banknum];
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001543
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001544 if (board_flash_get_legacy(base, banknum, info)) {
1545 /* board code may have filled info completely. If not, we
1546 use JEDEC ID probing. */
1547 if (!info->vendor) {
1548 int modes[] = {
1549 CFI_CMDSET_AMD_STANDARD,
1550 CFI_CMDSET_INTEL_STANDARD
1551 };
1552 int i;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001553
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001554 for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) {
1555 info->vendor = modes[i];
1556 info->start[0] = base;
1557 if (info->portwidth == FLASH_CFI_8BIT
1558 && info->interface == FLASH_CFI_X8X16) {
1559 info->addr_unlock1 = 0x2AAA;
1560 info->addr_unlock2 = 0x5555;
1561 } else {
1562 info->addr_unlock1 = 0x5555;
1563 info->addr_unlock2 = 0x2AAA;
1564 }
1565 flash_read_jedec_ids(info);
1566 debug("JEDEC PROBE: ID %x %x %x\n",
1567 info->manufacturer_id,
1568 info->device_id,
1569 info->device_id2);
1570 if (jedec_flash_match(info, base))
1571 break;
1572 }
1573 }
1574
1575 switch(info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001576 case CFI_CMDSET_INTEL_PROG_REGIONS:
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001577 case CFI_CMDSET_INTEL_STANDARD:
1578 case CFI_CMDSET_INTEL_EXTENDED:
1579 info->cmd_reset = FLASH_CMD_RESET;
1580 break;
1581 case CFI_CMDSET_AMD_STANDARD:
1582 case CFI_CMDSET_AMD_EXTENDED:
1583 case CFI_CMDSET_AMD_LEGACY:
1584 info->cmd_reset = AMD_CMD_RESET;
1585 break;
1586 }
1587 info->flash_id = FLASH_MAN_CFI;
1588 return 1;
1589 }
1590 return 0; /* use CFI */
1591}
1592#else
1593static inline int flash_detect_legacy(ulong base, int banknum)
1594{
1595 return 0; /* use CFI */
1596}
1597#endif
1598
1599/*-----------------------------------------------------------------------
1600 * detect if flash is compatible with the Common Flash Interface (CFI)
1601 * http://www.jedec.org/download/search/jesd68.pdf
1602 */
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001603static void flash_read_cfi (flash_info_t *info, void *buf,
1604 unsigned int start, size_t len)
1605{
1606 u8 *p = buf;
1607 unsigned int i;
1608
1609 for (i = 0; i < len; i++)
1610 p[i] = flash_read_uchar(info, start + i);
1611}
1612
1613static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001614{
1615 int cfi_offset;
1616
Michael Schwingen4661cf72008-02-18 23:16:35 +01001617 /* We do not yet know what kind of commandset to use, so we issue
1618 the reset command in both Intel and AMD variants, in the hope
1619 that AMD flash roms ignore the Intel command. */
1620 flash_write_cmd (info, 0, 0, AMD_CMD_RESET);
1621 flash_write_cmd (info, 0, 0, FLASH_CMD_RESET);
1622
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001623 for (cfi_offset=0;
1624 cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint);
1625 cfi_offset++) {
1626 flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],
1627 FLASH_CMD_CFI);
1628 if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q')
1629 && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R')
1630 && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) {
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001631 flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,
1632 sizeof(struct cfi_qry));
1633 info->interface = le16_to_cpu(qry->interface_desc);
1634
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001635 info->cfi_offset = flash_offset_cfi[cfi_offset];
1636 debug ("device interface is %d\n",
1637 info->interface);
1638 debug ("found port %d chip %d ",
1639 info->portwidth, info->chipwidth);
1640 debug ("port %d bits chip %d bits\n",
1641 info->portwidth << CFI_FLASH_SHIFT_WIDTH,
1642 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
1643
1644 /* calculate command offsets as in the Linux driver */
1645 info->addr_unlock1 = 0x555;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001646 info->addr_unlock2 = 0x2aa;
1647
1648 /*
1649 * modify the unlock address if we are
1650 * in compatibility mode
1651 */
1652 if ( /* x8/x16 in x8 mode */
1653 ((info->chipwidth == FLASH_CFI_BY8) &&
1654 (info->interface == FLASH_CFI_X8X16)) ||
1655 /* x16/x32 in x16 mode */
1656 ((info->chipwidth == FLASH_CFI_BY16) &&
1657 (info->interface == FLASH_CFI_X16X32)))
1658 {
1659 info->addr_unlock1 = 0xaaa;
1660 info->addr_unlock2 = 0x555;
1661 }
1662
1663 info->name = "CFI conformant";
1664 return 1;
1665 }
1666 }
1667
1668 return 0;
1669}
1670
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001671static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001672{
wdenke65527f2004-02-12 00:47:09 +00001673 debug ("flash detect cfi\n");
wdenk2cefd152004-02-08 22:55:38 +00001674
Stefan Roesec865e6c2006-02-28 15:29:58 +01001675 for (info->portwidth = CFG_FLASH_CFI_WIDTH;
wdenke65527f2004-02-12 00:47:09 +00001676 info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) {
1677 for (info->chipwidth = FLASH_CFI_BY8;
1678 info->chipwidth <= info->portwidth;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001679 info->chipwidth <<= 1)
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001680 if (__flash_detect_cfi(info, qry))
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001681 return 1;
wdenk2cefd152004-02-08 22:55:38 +00001682 }
wdenke65527f2004-02-12 00:47:09 +00001683 debug ("not found\n");
wdenk2cefd152004-02-08 22:55:38 +00001684 return 0;
1685}
wdenke65527f2004-02-12 00:47:09 +00001686
wdenk2cefd152004-02-08 22:55:38 +00001687/*
Haavard Skinnemoen750ea7f2007-12-14 15:36:18 +01001688 * Manufacturer-specific quirks. Add workarounds for geometry
1689 * reversal, etc. here.
1690 */
1691static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry)
1692{
1693 /* check if flash geometry needs reversal */
1694 if (qry->num_erase_regions > 1) {
1695 /* reverse geometry if top boot part */
1696 if (info->cfi_version < 0x3131) {
1697 /* CFI < 1.1, try to guess from device id */
1698 if ((info->device_id & 0x80) != 0)
1699 cfi_reverse_geometry(qry);
1700 } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) {
1701 /* CFI >= 1.1, deduct from top/bottom flag */
1702 /* note: ext_addr is valid since cfi_version > 0 */
1703 cfi_reverse_geometry(qry);
1704 }
1705 }
1706}
1707
1708static void flash_fixup_atmel(flash_info_t *info, struct cfi_qry *qry)
1709{
1710 int reverse_geometry = 0;
1711
1712 /* Check the "top boot" bit in the PRI */
1713 if (info->ext_addr && !(flash_read_uchar(info, info->ext_addr + 6) & 1))
1714 reverse_geometry = 1;
1715
1716 /* AT49BV6416(T) list the erase regions in the wrong order.
1717 * However, the device ID is identical with the non-broken
1718 * AT49BV642D since u-boot only reads the low byte (they
1719 * differ in the high byte.) So leave out this fixup for now.
1720 */
1721#if 0
1722 if (info->device_id == 0xd6 || info->device_id == 0xd2)
1723 reverse_geometry = !reverse_geometry;
1724#endif
1725
1726 if (reverse_geometry)
1727 cfi_reverse_geometry(qry);
1728}
1729
1730/*
wdenk2cefd152004-02-08 22:55:38 +00001731 * The following code cannot be run from FLASH!
1732 *
1733 */
Marian Balakowicz513b4a12005-10-11 19:09:42 +02001734ulong flash_get_size (ulong base, int banknum)
wdenk2cefd152004-02-08 22:55:38 +00001735{
wdenke65527f2004-02-12 00:47:09 +00001736 flash_info_t *info = &flash_info[banknum];
wdenk2cefd152004-02-08 22:55:38 +00001737 int i, j;
1738 flash_sect_t sect_cnt;
1739 unsigned long sector;
1740 unsigned long tmp;
1741 int size_ratio;
1742 uchar num_erase_regions;
wdenke65527f2004-02-12 00:47:09 +00001743 int erase_region_size;
1744 int erase_region_count;
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001745 struct cfi_qry qry;
Stefan Roese12797482006-11-13 13:55:24 +01001746
Kumar Gala899032b2008-05-15 15:13:08 -05001747 memset(&qry, 0, sizeof(qry));
1748
Stefan Roese12797482006-11-13 13:55:24 +01001749 info->ext_addr = 0;
1750 info->cfi_version = 0;
Stefan Roeseefef95b2006-04-01 13:41:03 +02001751#ifdef CFG_FLASH_PROTECTION
Stefan Roeseefef95b2006-04-01 13:41:03 +02001752 info->legacy_unlock = 0;
1753#endif
wdenk2cefd152004-02-08 22:55:38 +00001754
1755 info->start[0] = base;
1756
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001757 if (flash_detect_cfi (info, &qry)) {
1758 info->vendor = le16_to_cpu(qry.p_id);
1759 info->ext_addr = le16_to_cpu(qry.p_adr);
1760 num_erase_regions = qry.num_erase_regions;
1761
Stefan Roese12797482006-11-13 13:55:24 +01001762 if (info->ext_addr) {
1763 info->cfi_version = (ushort) flash_read_uchar (info,
1764 info->ext_addr + 3) << 8;
1765 info->cfi_version |= (ushort) flash_read_uchar (info,
1766 info->ext_addr + 4);
1767 }
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001768
wdenke65527f2004-02-12 00:47:09 +00001769#ifdef DEBUG
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001770 flash_printqry (&qry);
wdenke65527f2004-02-12 00:47:09 +00001771#endif
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001772
wdenke65527f2004-02-12 00:47:09 +00001773 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001774 case CFI_CMDSET_INTEL_PROG_REGIONS:
wdenk2cefd152004-02-08 22:55:38 +00001775 case CFI_CMDSET_INTEL_STANDARD:
1776 case CFI_CMDSET_INTEL_EXTENDED:
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001777 cmdset_intel_init(info, &qry);
wdenk2cefd152004-02-08 22:55:38 +00001778 break;
1779 case CFI_CMDSET_AMD_STANDARD:
1780 case CFI_CMDSET_AMD_EXTENDED:
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001781 cmdset_amd_init(info, &qry);
wdenk2cefd152004-02-08 22:55:38 +00001782 break;
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001783 default:
1784 printf("CFI: Unknown command set 0x%x\n",
1785 info->vendor);
1786 /*
1787 * Unfortunately, this means we don't know how
1788 * to get the chip back to Read mode. Might
1789 * as well try an Intel-style reset...
1790 */
1791 flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
1792 return 0;
wdenk2cefd152004-02-08 22:55:38 +00001793 }
wdenk6cfa84e2004-02-10 00:03:41 +00001794
Haavard Skinnemoen750ea7f2007-12-14 15:36:18 +01001795 /* Do manufacturer-specific fixups */
1796 switch (info->manufacturer_id) {
1797 case 0x0001:
1798 flash_fixup_amd(info, &qry);
1799 break;
1800 case 0x001f:
1801 flash_fixup_atmel(info, &qry);
1802 break;
1803 }
1804
wdenke65527f2004-02-12 00:47:09 +00001805 debug ("manufacturer is %d\n", info->vendor);
Stefan Roese12797482006-11-13 13:55:24 +01001806 debug ("manufacturer id is 0x%x\n", info->manufacturer_id);
1807 debug ("device id is 0x%x\n", info->device_id);
1808 debug ("device id2 is 0x%x\n", info->device_id2);
1809 debug ("cfi version is 0x%04x\n", info->cfi_version);
1810
wdenk2cefd152004-02-08 22:55:38 +00001811 size_ratio = info->portwidth / info->chipwidth;
wdenke65527f2004-02-12 00:47:09 +00001812 /* if the chip is x8/x16 reduce the ratio by half */
1813 if ((info->interface == FLASH_CFI_X8X16)
1814 && (info->chipwidth == FLASH_CFI_BY8)) {
1815 size_ratio >>= 1;
1816 }
wdenke65527f2004-02-12 00:47:09 +00001817 debug ("size_ratio %d port %d bits chip %d bits\n",
1818 size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH,
1819 info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
1820 debug ("found %d erase regions\n", num_erase_regions);
wdenk2cefd152004-02-08 22:55:38 +00001821 sect_cnt = 0;
1822 sector = base;
wdenke65527f2004-02-12 00:47:09 +00001823 for (i = 0; i < num_erase_regions; i++) {
1824 if (i > NUM_ERASE_REGIONS) {
wdenke537b3b2004-02-23 23:54:43 +00001825 printf ("%d erase regions found, only %d used\n",
1826 num_erase_regions, NUM_ERASE_REGIONS);
wdenk2cefd152004-02-08 22:55:38 +00001827 break;
1828 }
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001829
Haavard Skinnemoenc4d478b2007-12-14 15:36:17 +01001830 tmp = le32_to_cpu(qry.erase_region_info[i]);
1831 debug("erase region %u: 0x%08lx\n", i, tmp);
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001832
1833 erase_region_count = (tmp & 0xffff) + 1;
1834 tmp >>= 16;
wdenke65527f2004-02-12 00:47:09 +00001835 erase_region_size =
1836 (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128;
wdenkaeba06f2004-06-09 17:34:58 +00001837 debug ("erase_region_count = %d erase_region_size = %d\n",
wdenke537b3b2004-02-23 23:54:43 +00001838 erase_region_count, erase_region_size);
wdenke65527f2004-02-12 00:47:09 +00001839 for (j = 0; j < erase_region_count; j++) {
Michael Schwingen73d044d2007-12-07 23:35:02 +01001840 if (sect_cnt >= CFG_MAX_FLASH_SECT) {
1841 printf("ERROR: too many flash sectors\n");
1842 break;
1843 }
wdenk2cefd152004-02-08 22:55:38 +00001844 info->start[sect_cnt] = sector;
1845 sector += (erase_region_size * size_ratio);
wdenk26c58432005-01-09 17:12:27 +00001846
1847 /*
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001848 * Only read protection status from
1849 * supported devices (intel...)
wdenk26c58432005-01-09 17:12:27 +00001850 */
1851 switch (info->vendor) {
Vasiliy Leoenenko7d1794c2008-05-07 21:25:33 +04001852 case CFI_CMDSET_INTEL_PROG_REGIONS:
wdenk26c58432005-01-09 17:12:27 +00001853 case CFI_CMDSET_INTEL_EXTENDED:
1854 case CFI_CMDSET_INTEL_STANDARD:
1855 info->protect[sect_cnt] =
1856 flash_isset (info, sect_cnt,
1857 FLASH_OFFSET_PROTECT,
1858 FLASH_STATUS_PROTECT);
1859 break;
1860 default:
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001861 /* default: not protected */
1862 info->protect[sect_cnt] = 0;
wdenk26c58432005-01-09 17:12:27 +00001863 }
1864
wdenk2cefd152004-02-08 22:55:38 +00001865 sect_cnt++;
1866 }
1867 }
1868
1869 info->sector_count = sect_cnt;
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001870 info->size = 1 << qry.dev_size;
wdenk2cefd152004-02-08 22:55:38 +00001871 /* multiply the size by the number of chips */
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001872 info->size *= size_ratio;
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001873 info->buffer_size = 1 << le16_to_cpu(qry.max_buf_write_size);
1874 tmp = 1 << qry.block_erase_timeout_typ;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001875 info->erase_blk_tout = tmp *
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001876 (1 << qry.block_erase_timeout_max);
1877 tmp = (1 << qry.buf_write_timeout_typ) *
1878 (1 << qry.buf_write_timeout_max);
1879
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001880 /* round up when converting to ms */
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001881 info->buffer_write_tout = (tmp + 999) / 1000;
1882 tmp = (1 << qry.word_write_timeout_typ) *
1883 (1 << qry.word_write_timeout_max);
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001884 /* round up when converting to ms */
Haavard Skinnemoen53baec72007-12-14 15:36:16 +01001885 info->write_tout = (tmp + 999) / 1000;
wdenk2cefd152004-02-08 22:55:38 +00001886 info->flash_id = FLASH_MAN_CFI;
Haavard Skinnemoend523e392007-12-13 12:56:28 +01001887 if ((info->interface == FLASH_CFI_X8X16) &&
1888 (info->chipwidth == FLASH_CFI_BY8)) {
1889 /* XXX - Need to test on x8/x16 in parallel. */
1890 info->portwidth >>= 1;
wdenked2ac4b2004-03-14 18:23:55 +00001891 }
wdenk2cefd152004-02-08 22:55:38 +00001892 }
1893
Wolfgang Denka205a8f2005-09-25 16:41:22 +02001894 flash_write_cmd (info, 0, 0, info->cmd_reset);
wdenke65527f2004-02-12 00:47:09 +00001895 return (info->size);
wdenk2cefd152004-02-08 22:55:38 +00001896}
1897
wdenk2cefd152004-02-08 22:55:38 +00001898/*-----------------------------------------------------------------------
1899 */
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001900unsigned long flash_init (void)
wdenk2cefd152004-02-08 22:55:38 +00001901{
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001902 unsigned long size = 0;
1903 int i;
Matthias Fuchs50431522008-04-18 16:29:40 +02001904#if defined(CFG_FLASH_AUTOPROTECT_LIST)
1905 struct apl_s {
1906 ulong start;
1907 ulong size;
1908 } apl[] = CFG_FLASH_AUTOPROTECT_LIST;
1909#endif
wdenk2cefd152004-02-08 22:55:38 +00001910
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001911#ifdef CFG_FLASH_PROTECTION
1912 char *s = getenv("unlock");
Michael Schwingen73d044d2007-12-07 23:35:02 +01001913#endif
wdenk2cefd152004-02-08 22:55:38 +00001914
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001915 /* Init: no FLASHes known */
1916 for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) {
1917 flash_info[i].flash_id = FLASH_UNKNOWN;
wdenk2cefd152004-02-08 22:55:38 +00001918
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001919 if (!flash_detect_legacy (bank_base[i], i))
1920 flash_get_size (bank_base[i], i);
1921 size += flash_info[i].size;
1922 if (flash_info[i].flash_id == FLASH_UNKNOWN) {
1923#ifndef CFG_FLASH_QUIET_TEST
1924 printf ("## Unknown FLASH on Bank %d "
1925 "- Size = 0x%08lx = %ld MB\n",
1926 i+1, flash_info[i].size,
1927 flash_info[i].size << 20);
1928#endif /* CFG_FLASH_QUIET_TEST */
1929 }
1930#ifdef CFG_FLASH_PROTECTION
1931 else if ((s != NULL) && (strcmp(s, "yes") == 0)) {
1932 /*
1933 * Only the U-Boot image and it's environment
1934 * is protected, all other sectors are
1935 * unprotected (unlocked) if flash hardware
1936 * protection is used (CFG_FLASH_PROTECTION)
1937 * and the environment variable "unlock" is
1938 * set to "yes".
1939 */
1940 if (flash_info[i].legacy_unlock) {
1941 int k;
wdenk2cefd152004-02-08 22:55:38 +00001942
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001943 /*
1944 * Disable legacy_unlock temporarily,
1945 * since flash_real_protect would
1946 * relock all other sectors again
1947 * otherwise.
1948 */
1949 flash_info[i].legacy_unlock = 0;
wdenk2cefd152004-02-08 22:55:38 +00001950
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001951 /*
1952 * Legacy unlocking (e.g. Intel J3) ->
1953 * unlock only one sector. This will
1954 * unlock all sectors.
1955 */
1956 flash_real_protect (&flash_info[i], 0, 0);
wdenk2cefd152004-02-08 22:55:38 +00001957
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001958 flash_info[i].legacy_unlock = 1;
wdenk2cefd152004-02-08 22:55:38 +00001959
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001960 /*
1961 * Manually mark other sectors as
1962 * unlocked (unprotected)
1963 */
1964 for (k = 1; k < flash_info[i].sector_count; k++)
1965 flash_info[i].protect[k] = 0;
1966 } else {
1967 /*
1968 * No legancy unlocking -> unlock all sectors
1969 */
1970 flash_protect (FLAG_PROTECT_CLEAR,
1971 flash_info[i].start[0],
1972 flash_info[i].start[0]
1973 + flash_info[i].size - 1,
1974 &flash_info[i]);
Stefan Roesec865e6c2006-02-28 15:29:58 +01001975 }
Stefan Roesec865e6c2006-02-28 15:29:58 +01001976 }
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001977#endif /* CFG_FLASH_PROTECTION */
1978 }
Stefan Roesec865e6c2006-02-28 15:29:58 +01001979
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001980 /* Monitor protection ON by default */
1981#if (CFG_MONITOR_BASE >= CFG_FLASH_BASE)
1982 flash_protect (FLAG_PROTECT_SET,
1983 CFG_MONITOR_BASE,
1984 CFG_MONITOR_BASE + monitor_flash_len - 1,
1985 flash_get_info(CFG_MONITOR_BASE));
1986#endif
Stefan Roesec865e6c2006-02-28 15:29:58 +01001987
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001988 /* Environment protection ON by default */
1989#ifdef CFG_ENV_IS_IN_FLASH
1990 flash_protect (FLAG_PROTECT_SET,
1991 CFG_ENV_ADDR,
1992 CFG_ENV_ADDR + CFG_ENV_SECT_SIZE - 1,
1993 flash_get_info(CFG_ENV_ADDR));
1994#endif
Stefan Roesec865e6c2006-02-28 15:29:58 +01001995
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02001996 /* Redundant environment protection ON by default */
1997#ifdef CFG_ENV_ADDR_REDUND
1998 flash_protect (FLAG_PROTECT_SET,
1999 CFG_ENV_ADDR_REDUND,
2000 CFG_ENV_ADDR_REDUND + CFG_ENV_SIZE_REDUND - 1,
2001 flash_get_info(CFG_ENV_ADDR_REDUND));
2002#endif
Matthias Fuchs50431522008-04-18 16:29:40 +02002003
2004#if defined(CFG_FLASH_AUTOPROTECT_LIST)
2005 for (i = 0; i < (sizeof(apl) / sizeof(struct apl_s)); i++) {
2006 debug("autoprotecting from %08x to %08x\n",
2007 apl[i].start, apl[i].start + apl[i].size - 1);
2008 flash_protect (FLAG_PROTECT_SET,
2009 apl[i].start,
2010 apl[i].start + apl[i].size - 1,
2011 flash_get_info(apl[i].start));
2012 }
2013#endif
Haavard Skinnemoen6dd763c2007-10-06 18:55:36 +02002014 return (size);
wdenk2cefd152004-02-08 22:55:38 +00002015}
Heiko Schocher3c58a992007-01-11 15:44:44 +01002016
wdenk2cefd152004-02-08 22:55:38 +00002017#endif /* CFG_FLASH_CFI */