1 |
786 |
skrzyp |
//==========================================================================
|
2 |
|
|
//
|
3 |
|
|
// am29xxxxx_aux.c
|
4 |
|
|
//
|
5 |
|
|
// Flash driver for the AMD family - implementation.
|
6 |
|
|
//
|
7 |
|
|
//==========================================================================
|
8 |
|
|
// ####ECOSGPLCOPYRIGHTBEGIN####
|
9 |
|
|
// -------------------------------------------
|
10 |
|
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
11 |
|
|
// Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
12 |
|
|
//
|
13 |
|
|
// eCos is free software; you can redistribute it and/or modify it under
|
14 |
|
|
// the terms of the GNU General Public License as published by the Free
|
15 |
|
|
// Software Foundation; either version 2 or (at your option) any later
|
16 |
|
|
// version.
|
17 |
|
|
//
|
18 |
|
|
// eCos is distributed in the hope that it will be useful, but WITHOUT
|
19 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
20 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
21 |
|
|
// for more details.
|
22 |
|
|
//
|
23 |
|
|
// You should have received a copy of the GNU General Public License
|
24 |
|
|
// along with eCos; if not, write to the Free Software Foundation, Inc.,
|
25 |
|
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
26 |
|
|
//
|
27 |
|
|
// As a special exception, if other files instantiate templates or use
|
28 |
|
|
// macros or inline functions from this file, or you compile this file
|
29 |
|
|
// and link it with other works to produce a work based on this file,
|
30 |
|
|
// this file does not by itself cause the resulting work to be covered by
|
31 |
|
|
// the GNU General Public License. However the source code for this file
|
32 |
|
|
// must still be made available in accordance with section (3) of the GNU
|
33 |
|
|
// General Public License v2.
|
34 |
|
|
//
|
35 |
|
|
// This exception does not invalidate any other reasons why a work based
|
36 |
|
|
// on this file might be covered by the GNU General Public License.
|
37 |
|
|
// -------------------------------------------
|
38 |
|
|
// ####ECOSGPLCOPYRIGHTEND####
|
39 |
|
|
//==========================================================================
|
40 |
|
|
//#####DESCRIPTIONBEGIN####
|
41 |
|
|
//
|
42 |
|
|
// Author(s): bartv
|
43 |
|
|
// Contributors:
|
44 |
|
|
// Date: 2004-11-05
|
45 |
|
|
//
|
46 |
|
|
//####DESCRIPTIONEND####
|
47 |
|
|
//
|
48 |
|
|
//==========================================================================
|
49 |
|
|
|
50 |
|
|
// This file is #include'd multiple times from the main am29xxxxx.c file,
|
51 |
|
|
// It serves to instantiate the various hardware operations in ways
|
52 |
|
|
// appropriate for all the bus configurations.
|
53 |
|
|
|
54 |
|
|
// The following macros are used to construct suitable function names
|
55 |
|
|
// for the current bus configuration. AM29_SUFFIX is #define'd before
|
56 |
|
|
// each #include of am29xxxxx_aux.c
|
57 |
|
|
|
58 |
|
|
#ifndef AM29_STR
|
59 |
|
|
# define AM29_STR1(_a_) # _a_
|
60 |
|
|
# define AM29_STR(_a_) AM29_STR1(_a_)
|
61 |
|
|
# define AM29_CONCAT3_AUX(_a_, _b_, _c_) _a_##_b_##_c_
|
62 |
|
|
# define AM29_CONCAT3(_a_, _b_, _c_) AM29_CONCAT3_AUX(_a_, _b_, _c_)
|
63 |
|
|
#endif
|
64 |
|
|
|
65 |
|
|
#define AM29_FNNAME(_base_) AM29_CONCAT3(_base_, _, AM29_SUFFIX)
|
66 |
|
|
|
67 |
|
|
// Similarly construct a forward declaration, placing the function in
|
68 |
|
|
// the .2ram section. Each function must still be in a separate section
|
69 |
|
|
// for linker garbage collection.
|
70 |
|
|
|
71 |
|
|
# define AM29_RAMFNDECL(_base_, _args_) \
|
72 |
|
|
AM29_FNNAME(_base_) _args_ __attribute__((section (".2ram." AM29_STR(_base_) "_" AM29_STR(AM29_SUFFIX))))
|
73 |
|
|
|
74 |
|
|
// Calculate the various offsets, based on the device count.
|
75 |
|
|
// The main code may override these settings for specific
|
76 |
|
|
// configurations, e.g. 16as8
|
77 |
|
|
#ifndef AM29_OFFSET_COMMAND
|
78 |
|
|
# define AM29_OFFSET_COMMAND 0x0555
|
79 |
|
|
#endif
|
80 |
|
|
#ifndef AM29_OFFSET_COMMAND2
|
81 |
|
|
# define AM29_OFFSET_COMMAND2 0x02AA
|
82 |
|
|
#endif
|
83 |
|
|
#ifndef AM29_OFFSET_MANUFACTURER_ID
|
84 |
|
|
# define AM29_OFFSET_MANUFACTURER_ID 0x0000
|
85 |
|
|
#endif
|
86 |
|
|
#ifndef AM29_OFFSET_DEVID
|
87 |
|
|
# define AM29_OFFSET_DEVID 0x0001
|
88 |
|
|
#endif
|
89 |
|
|
#ifndef AM29_OFFSET_DEVID2
|
90 |
|
|
# define AM29_OFFSET_DEVID2 0x000E
|
91 |
|
|
#endif
|
92 |
|
|
#ifndef AM29_OFFSET_DEVID3
|
93 |
|
|
# define AM29_OFFSET_DEVID3 0x000F
|
94 |
|
|
#endif
|
95 |
|
|
#ifndef AM29_OFFSET_CFI
|
96 |
|
|
# define AM29_OFFSET_CFI 0x0055
|
97 |
|
|
#endif
|
98 |
|
|
#ifndef AM29_OFFSET_CFI_DATA
|
99 |
|
|
# define AM29_OFFSET_CFI_DATA(_idx_) _idx_
|
100 |
|
|
#endif
|
101 |
|
|
#ifndef AM29_OFFSET_AT49_LOCK_STATUS
|
102 |
|
|
# define AM29_OFFSET_AT49_LOCK_STATUS 0x02
|
103 |
|
|
#endif
|
104 |
|
|
|
105 |
|
|
// For parallel operation commands are issued in parallel and status
|
106 |
|
|
// bits are checked in parallel.
|
107 |
|
|
#ifndef AM29_PARALLEL
|
108 |
|
|
# define AM29_PARALLEL(_cmd_) (_cmd_)
|
109 |
|
|
#endif
|
110 |
|
|
|
111 |
|
|
// ----------------------------------------------------------------------------
|
112 |
|
|
// Diagnostic routines.
|
113 |
|
|
|
114 |
|
|
#if 0
|
115 |
|
|
#define amd_diag( __fmt, ... ) diag_printf("AMD: %s[%d]: " __fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__ );
|
116 |
|
|
#define amd_dump_buf( __addr, __size ) diag_dump_buf( __addr, __size )
|
117 |
|
|
#else
|
118 |
|
|
#define amd_diag( __fmt, ... )
|
119 |
|
|
#define amd_dump_buf( __addr, __size )
|
120 |
|
|
#endif
|
121 |
|
|
|
122 |
|
|
// ----------------------------------------------------------------------------
|
123 |
|
|
// When performing the various low-level operations like erase the flash
|
124 |
|
|
// chip can no longer support ordinary data reads. Obviously this is a
|
125 |
|
|
// problem if the current code is executing out of flash. The solution is
|
126 |
|
|
// to store the key functions in RAM rather than flash, via a special
|
127 |
|
|
// linker section .2ram which usually gets placed in the same area as
|
128 |
|
|
// .data.
|
129 |
|
|
//
|
130 |
|
|
// In a ROM startup application anything in .2ram will consume space
|
131 |
|
|
// in both the flash and RAM. Hence it is desirable to keep the .2ram
|
132 |
|
|
// functions as small as possible, responsible only for the actual
|
133 |
|
|
// hardware manipulation.
|
134 |
|
|
//
|
135 |
|
|
// All these .2ram functions should be invoked with interrupts
|
136 |
|
|
// disabled. Depending on the hardware it may also be necessary to
|
137 |
|
|
// have the data cache disabled. The .2ram functions must be
|
138 |
|
|
// self-contained, even macro invocations like HAL_DELAY_US() are
|
139 |
|
|
// banned because on some platforms those could be implemented as
|
140 |
|
|
// function calls.
|
141 |
|
|
|
142 |
|
|
// gcc requires forward declarations with the attributes, then the actual
|
143 |
|
|
// definitions.
|
144 |
|
|
static int AM29_RAMFNDECL(am29_hw_query, (volatile AM29_TYPE*));
|
145 |
|
|
static int AM29_RAMFNDECL(am29_hw_cfi, (struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*));
|
146 |
|
|
static int AM29_RAMFNDECL(am29_hw_erase, (volatile AM29_TYPE*));
|
147 |
|
|
static int AM29_RAMFNDECL(am29_hw_program, (volatile AM29_TYPE*, volatile AM29_TYPE*, const cyg_uint8*, cyg_uint32 count, int retries));
|
148 |
|
|
static int AM29_RAMFNDECL(at49_hw_softlock, (volatile AM29_TYPE*));
|
149 |
|
|
static int AM29_RAMFNDECL(at49_hw_hardlock, (volatile AM29_TYPE*));
|
150 |
|
|
static int AM29_RAMFNDECL(at49_hw_unlock, (volatile AM29_TYPE*));
|
151 |
|
|
|
152 |
|
|
|
153 |
|
|
// ----------------------------------------------------------------------------
|
154 |
|
|
|
155 |
|
|
#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME
|
156 |
|
|
// With this flash component (e.g. AT49xxxxx), the reset does not
|
157 |
|
|
// cause a suspended erase/program to be aborted. Instead all we
|
158 |
|
|
// can do is resume any suspended operations. We do this on each
|
159 |
|
|
// block as some parts have different granularity.
|
160 |
|
|
|
161 |
|
|
static void
|
162 |
|
|
AM29_FNNAME(am29_hw_force_all_suspended_resume)(struct cyg_flash_dev* dev, cyg_am29xxxxx_dev* am29_dev, volatile AM29_TYPE* addr)
|
163 |
|
|
{
|
164 |
|
|
cyg_ucount16 i,j;
|
165 |
|
|
AM29_TYPE datum1, datum2;
|
166 |
|
|
|
167 |
|
|
AM29_2RAM_ENTRY_HOOK();
|
168 |
|
|
|
169 |
|
|
for (i=0; i<dev->num_block_infos; i++)
|
170 |
|
|
{
|
171 |
|
|
for (j=0; j<am29_dev->block_info[i].blocks; j++)
|
172 |
|
|
{
|
173 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_ERASE_RESUME;
|
174 |
|
|
HAL_MEMORY_BARRIER();
|
175 |
|
|
// We don't know if the suspended operation was an erase or
|
176 |
|
|
// program, so just compare the whole word to spot _any_ toggling.
|
177 |
|
|
do {
|
178 |
|
|
datum1 = addr[AM29_OFFSET_COMMAND];
|
179 |
|
|
datum2 = addr[AM29_OFFSET_COMMAND];
|
180 |
|
|
} while (datum1 != datum2);
|
181 |
|
|
|
182 |
|
|
addr += am29_dev->block_info[i].block_size/sizeof(AM29_TYPE);
|
183 |
|
|
}
|
184 |
|
|
}
|
185 |
|
|
|
186 |
|
|
AM29_2RAM_EXIT_HOOK();
|
187 |
|
|
}
|
188 |
|
|
#endif // ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME
|
189 |
|
|
|
190 |
|
|
// Read the device id. This involves a straightforward command
|
191 |
|
|
// sequence, followed by a reset to get back into array mode.
|
192 |
|
|
// All chips are accessed in parallel, but only the response
|
193 |
|
|
// from the least significant is used.
|
194 |
|
|
static int
|
195 |
|
|
AM29_FNNAME(am29_hw_query)(volatile AM29_TYPE* addr)
|
196 |
|
|
{
|
197 |
|
|
int devid;
|
198 |
|
|
cyg_uint32 onedevmask;
|
199 |
|
|
|
200 |
|
|
AM29_2RAM_ENTRY_HOOK();
|
201 |
|
|
|
202 |
|
|
// Fortunately the compiler should optimise the below
|
203 |
|
|
// tests such that onedevmask is a constant.
|
204 |
|
|
if ( 1 == (sizeof(AM29_TYPE) / AM29_DEVCOUNT) )
|
205 |
|
|
onedevmask = 0xFF;
|
206 |
|
|
else if ( 2 == (sizeof(AM29_TYPE) / AM29_DEVCOUNT) )
|
207 |
|
|
onedevmask = 0xFFFF;
|
208 |
|
|
else {
|
209 |
|
|
CYG_ASSERT( 4 == (sizeof(AM29_TYPE) / AM29_DEVCOUNT),
|
210 |
|
|
"Unexpected flash width per device" );
|
211 |
|
|
onedevmask = 0xFFFFFFFF;
|
212 |
|
|
}
|
213 |
|
|
|
214 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
215 |
|
|
HAL_MEMORY_BARRIER();
|
216 |
|
|
addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
217 |
|
|
HAL_MEMORY_BARRIER();
|
218 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_AUTOSELECT;
|
219 |
|
|
HAL_MEMORY_BARRIER();
|
220 |
|
|
|
221 |
|
|
devid = AM29_UNSWAP(addr[AM29_OFFSET_DEVID]) & onedevmask;
|
222 |
|
|
|
223 |
|
|
// amd_diag("devid %x\n", devid );
|
224 |
|
|
// amd_dump_buf(addr, 64 );
|
225 |
|
|
|
226 |
|
|
// The original AMD chips only used a single-byte device id, but
|
227 |
|
|
// all codes have now been used up. Newer devices use a 3-byte
|
228 |
|
|
// devid. The above devid read will have returned 0x007E. The
|
229 |
|
|
// test allows for boards with a mixture of old and new chips.
|
230 |
|
|
// The amount of code involved is too small to warrant a config
|
231 |
|
|
// option.
|
232 |
|
|
// FIXME by jifl: What happens when a single device is connected 16-bits
|
233 |
|
|
// (or 32-bits) wide per device? Is the code still 0x7E, and all the
|
234 |
|
|
// other devids are 8-bits only?
|
235 |
|
|
if (0x007E == devid) {
|
236 |
|
|
devid <<= 16;
|
237 |
|
|
devid |= ((AM29_UNSWAP(addr[AM29_OFFSET_DEVID2]) & 0x00FF) << 8);
|
238 |
|
|
devid |= (AM29_UNSWAP(addr[AM29_OFFSET_DEVID3]) & 0x00FF);
|
239 |
|
|
}
|
240 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET;
|
241 |
|
|
HAL_MEMORY_BARRIER();
|
242 |
|
|
|
243 |
|
|
// amd_diag("devid %x\n", devid );
|
244 |
|
|
|
245 |
|
|
AM29_2RAM_EXIT_HOOK();
|
246 |
|
|
return devid;
|
247 |
|
|
}
|
248 |
|
|
|
249 |
|
|
// Perform a CFI query. This involves placing the device(s) into CFI
|
250 |
|
|
// mode, checking that this has really happened, and then reading the
|
251 |
|
|
// size and block info. The address corresponds to the start of the
|
252 |
|
|
// flash.
|
253 |
|
|
static int
|
254 |
|
|
AM29_FNNAME(am29_hw_cfi)(struct cyg_flash_dev* dev, cyg_am29xxxxx_dev* am29_dev, volatile AM29_TYPE* addr)
|
255 |
|
|
{
|
256 |
|
|
int dev_size;
|
257 |
|
|
int i;
|
258 |
|
|
int erase_regions;
|
259 |
|
|
|
260 |
|
|
AM29_2RAM_ENTRY_HOOK();
|
261 |
|
|
|
262 |
|
|
#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CFI_BOGOSITY
|
263 |
|
|
int manufacturer_id;
|
264 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
265 |
|
|
HAL_MEMORY_BARRIER();
|
266 |
|
|
addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
267 |
|
|
HAL_MEMORY_BARRIER();
|
268 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_AUTOSELECT;
|
269 |
|
|
HAL_MEMORY_BARRIER();
|
270 |
|
|
|
271 |
|
|
manufacturer_id = AM29_UNSWAP(addr[AM29_OFFSET_MANUFACTURER_ID]) & 0x00FF;
|
272 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET;
|
273 |
|
|
HAL_MEMORY_BARRIER();
|
274 |
|
|
#endif
|
275 |
|
|
|
276 |
|
|
// Just a single write is needed to put the device into CFI mode
|
277 |
|
|
addr[AM29_OFFSET_CFI] = AM29_COMMAND_CFI;
|
278 |
|
|
HAL_MEMORY_BARRIER();
|
279 |
|
|
// amd_diag("CFI data:\n");
|
280 |
|
|
// amd_dump_buf( addr, 256 );
|
281 |
|
|
// Now check that we really are in CFI mode. There should be a 'Q'
|
282 |
|
|
// at a specific address. This test is not 100% reliable, but should
|
283 |
|
|
// be good enough.
|
284 |
|
|
if ('Q' != (AM29_UNSWAP(addr[AM29_OFFSET_CFI_Q]) & 0x00FF)) {
|
285 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET;
|
286 |
|
|
HAL_MEMORY_BARRIER();
|
287 |
|
|
AM29_2RAM_EXIT_HOOK();
|
288 |
|
|
return CYG_FLASH_ERR_PROTOCOL;
|
289 |
|
|
}
|
290 |
|
|
// Device sizes are always a power of 2, and the shift is encoded
|
291 |
|
|
// in a single byte
|
292 |
|
|
dev_size = 0x01 << (AM29_UNSWAP(addr[AM29_OFFSET_CFI_SIZE]) & 0x00FF);
|
293 |
|
|
dev->end = dev->start + dev_size - 1;
|
294 |
|
|
|
295 |
|
|
// The number of erase regions is also encoded in a single byte.
|
296 |
|
|
// Usually this is no more than 4. A value of 0 indicates that
|
297 |
|
|
// only chip erase is supported, but the driver does not cope
|
298 |
|
|
// with that.
|
299 |
|
|
erase_regions = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_REGIONS]) & 0x00FF;
|
300 |
|
|
if (erase_regions > CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_REGIONS) {
|
301 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET;
|
302 |
|
|
HAL_MEMORY_BARRIER();
|
303 |
|
|
AM29_2RAM_EXIT_HOOK();
|
304 |
|
|
return CYG_FLASH_ERR_PROTOCOL;
|
305 |
|
|
}
|
306 |
|
|
dev->num_block_infos = erase_regions;
|
307 |
|
|
|
308 |
|
|
for (i = 0; i < erase_regions; i++) {
|
309 |
|
|
cyg_uint32 count, size;
|
310 |
|
|
cyg_uint32 count_lsb = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_COUNT_LSB(i)]) & 0x00FF;
|
311 |
|
|
cyg_uint32 count_msb = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_COUNT_MSB(i)]) & 0x00FF;
|
312 |
|
|
cyg_uint32 size_lsb = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_SIZE_LSB(i)]) & 0x00FF;
|
313 |
|
|
cyg_uint32 size_msb = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_SIZE_MSB(i)]) & 0x00FF;
|
314 |
|
|
|
315 |
|
|
count = ((count_msb << 8) | count_lsb) + 1;
|
316 |
|
|
size = (size_msb << 16) | (size_lsb << 8);
|
317 |
|
|
am29_dev->block_info[i].block_size = (size_t) size * AM29_DEVCOUNT;
|
318 |
|
|
am29_dev->block_info[i].blocks = count;
|
319 |
|
|
}
|
320 |
|
|
|
321 |
|
|
#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CFI_BOGOSITY
|
322 |
|
|
|
323 |
|
|
// Some flash parts have a peculiar implementation of CFI. The
|
324 |
|
|
// device erase regions may not be in the order specified in the
|
325 |
|
|
// main CFI area. Instead the erase regions are given in a
|
326 |
|
|
// manufacturer dependent fixed order, regardless of whether this
|
327 |
|
|
// is a top or bottom boot block device. A vendor-specific
|
328 |
|
|
// extended query block has an entry saying whether the boot
|
329 |
|
|
// blocks are at the top or bottom. This code works out whether
|
330 |
|
|
// the erase regions appear to be specified in the wrong order,
|
331 |
|
|
// and then swaps them over.
|
332 |
|
|
|
333 |
|
|
{
|
334 |
|
|
enum { bottom, symmetric, top } boot_type = symmetric;
|
335 |
|
|
cyg_uint32 vspec = AM29_SWAP(addr[AM29_OFFSET_CFI_DATA(0x15)]) & 0x00FF;
|
336 |
|
|
|
337 |
|
|
// Take a look at the vendor specific area for the boot block
|
338 |
|
|
// order.
|
339 |
|
|
|
340 |
|
|
switch( manufacturer_id )
|
341 |
|
|
{
|
342 |
|
|
// Atmel appear to have their own layout for the vendor
|
343 |
|
|
// specific area. Offset 0x06 of the vendor specific area
|
344 |
|
|
// contains a single bit: 0x00 = top boot, 0x01 = bottom
|
345 |
|
|
// boot. There appears to be no way of specifying
|
346 |
|
|
// symmetric formats.
|
347 |
|
|
case 0x1F:
|
348 |
|
|
if( (addr[AM29_OFFSET_CFI_DATA(vspec+0x06)] & AM29_SWAP(0x1)) == AM29_SWAP(0x1) )
|
349 |
|
|
boot_type = bottom;
|
350 |
|
|
else boot_type = top;
|
351 |
|
|
break;
|
352 |
|
|
|
353 |
|
|
// Most other manufacturers seem to follow the same layout
|
354 |
|
|
// and encoding. Offset 0xF of the vendor specific area
|
355 |
|
|
// contains the boot sector layout: 0x00 = uniform, 0x01 =
|
356 |
|
|
// 8x8k top and bottom, 0x02 = bottom boot, 0x03 = top
|
357 |
|
|
// boot, 0x04 = both top and bottom.
|
358 |
|
|
//
|
359 |
|
|
// The following manufacturers support this layout:
|
360 |
|
|
// AMD, Spansion, ST, Macronix.
|
361 |
|
|
default:
|
362 |
|
|
if( (addr[AM29_OFFSET_CFI_DATA(vspec+0xF)] == AM29_SWAP(0x2)) )
|
363 |
|
|
boot_type = bottom;
|
364 |
|
|
else if( (addr[AM29_OFFSET_CFI_DATA(vspec+0xF)] == AM29_SWAP(0x3)) )
|
365 |
|
|
boot_type = top;
|
366 |
|
|
// All other options are symmetric
|
367 |
|
|
break;
|
368 |
|
|
}
|
369 |
|
|
|
370 |
|
|
// If the device is marked as top boot, but the erase region
|
371 |
|
|
// list appears to be in bottom boot order, then reverse the
|
372 |
|
|
// list. Also swap, if it is marked as bottom boot but the
|
373 |
|
|
// erase regions appear to be in top boot order. This code
|
374 |
|
|
// assumes that the first boot block is always smaller than
|
375 |
|
|
// regular blocks; it is possible to imagine flash layouts for
|
376 |
|
|
// which that is not true.
|
377 |
|
|
|
378 |
|
|
if( ((boot_type == top) &&
|
379 |
|
|
(am29_dev->block_info[0].block_size < am29_dev->block_info[erase_regions-1].block_size)) ||
|
380 |
|
|
((boot_type == bottom) &&
|
381 |
|
|
(am29_dev->block_info[0].block_size > am29_dev->block_info[erase_regions-1].block_size)))
|
382 |
|
|
{
|
383 |
|
|
int lo, hi;
|
384 |
|
|
|
385 |
|
|
for( lo = 0, hi = erase_regions-1 ; lo < hi ; lo++, hi-- )
|
386 |
|
|
{
|
387 |
|
|
size_t size = am29_dev->block_info[lo].block_size;
|
388 |
|
|
cyg_uint32 count = am29_dev->block_info[lo].blocks;
|
389 |
|
|
am29_dev->block_info[lo].block_size = am29_dev->block_info[hi].block_size;
|
390 |
|
|
am29_dev->block_info[lo].blocks = am29_dev->block_info[hi].blocks;
|
391 |
|
|
am29_dev->block_info[hi].block_size = size;
|
392 |
|
|
am29_dev->block_info[hi].blocks = count;
|
393 |
|
|
}
|
394 |
|
|
}
|
395 |
|
|
}
|
396 |
|
|
#endif
|
397 |
|
|
|
398 |
|
|
// Get out of CFI mode
|
399 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET;
|
400 |
|
|
HAL_MEMORY_BARRIER();
|
401 |
|
|
|
402 |
|
|
AM29_2RAM_EXIT_HOOK();
|
403 |
|
|
return CYG_FLASH_ERR_OK;
|
404 |
|
|
}
|
405 |
|
|
|
406 |
|
|
// Erase a single sector. There is no API support for chip-erase. The
|
407 |
|
|
// generic code operates one sector at a time, invoking the driver for
|
408 |
|
|
// each sector, so there is no opportunity inside the driver for
|
409 |
|
|
// erasing multiple sectors in a single call. The address argument
|
410 |
|
|
// points at the start of the sector.
|
411 |
|
|
static int
|
412 |
|
|
AM29_FNNAME(am29_hw_erase)(volatile AM29_TYPE* addr)
|
413 |
|
|
{
|
414 |
|
|
int retries;
|
415 |
|
|
AM29_TYPE datum;
|
416 |
|
|
|
417 |
|
|
AM29_2RAM_ENTRY_HOOK();
|
418 |
|
|
|
419 |
|
|
// Start the erase operation
|
420 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
421 |
|
|
HAL_MEMORY_BARRIER();
|
422 |
|
|
addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
423 |
|
|
HAL_MEMORY_BARRIER();
|
424 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_ERASE;
|
425 |
|
|
HAL_MEMORY_BARRIER();
|
426 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
427 |
|
|
HAL_MEMORY_BARRIER();
|
428 |
|
|
addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
429 |
|
|
HAL_MEMORY_BARRIER();
|
430 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_ERASE_SECTOR;
|
431 |
|
|
HAL_MEMORY_BARRIER();
|
432 |
|
|
// There is now a 50us window in which we could send additional
|
433 |
|
|
// ERASE_SECTOR commands, but the driver API does not allow this
|
434 |
|
|
|
435 |
|
|
// All chips are now erasing in parallel. Loop until all have
|
436 |
|
|
// completed. This can be detected in a number of ways. The DQ7
|
437 |
|
|
// bit will be 0 until the erase is complete, but there is a
|
438 |
|
|
// problem if something went wrong (e.g. the sector is locked),
|
439 |
|
|
// the erase has not actually started, and the relevant bit was 0
|
440 |
|
|
// already. More useful is DQ6. This will toggle during the 50us
|
441 |
|
|
// window and while the erase is in progress, then stop toggling.
|
442 |
|
|
// If the erase does not actually start then the bit won't toggle
|
443 |
|
|
// at all so the operation completes rather quickly.
|
444 |
|
|
//
|
445 |
|
|
// If at any time DQ5 is set (indicating a timeout inside the
|
446 |
|
|
// chip) then a reset command must be issued and the erase is
|
447 |
|
|
// aborted. It is not clear this can actually happen during an
|
448 |
|
|
// erase, but just in case.
|
449 |
|
|
for (retries = CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_TIMEOUT;
|
450 |
|
|
retries > 0;
|
451 |
|
|
retries--) {
|
452 |
|
|
|
453 |
|
|
datum = addr[AM29_OFFSET_COMMAND];
|
454 |
|
|
// The operation completes when all DQ7 bits are set.
|
455 |
|
|
if ((datum & AM29_STATUS_DQ7) == AM29_STATUS_DQ7) {
|
456 |
|
|
break;
|
457 |
|
|
}
|
458 |
|
|
// Otherwise, for any flash chips where DQ7 is still clear, it is
|
459 |
|
|
// necessary to test DQ5.
|
460 |
|
|
if (((datum ^ AM29_STATUS_DQ7) >> 2) & datum & AM29_STATUS_DQ5) {
|
461 |
|
|
// DQ5 is set, indicating a hardware error. The calling code
|
462 |
|
|
// will always verify that the erase really was successful
|
463 |
|
|
// so we do not need to distinguish between error conditions.
|
464 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET;
|
465 |
|
|
HAL_MEMORY_BARRIER();
|
466 |
|
|
break;
|
467 |
|
|
}
|
468 |
|
|
}
|
469 |
|
|
|
470 |
|
|
// A result of 0 indicates a timeout.
|
471 |
|
|
// A non-zero result indicates
|
472 |
|
|
// that the erase completed or there has been a fatal error.
|
473 |
|
|
AM29_2RAM_EXIT_HOOK();
|
474 |
|
|
return retries;
|
475 |
|
|
}
|
476 |
|
|
|
477 |
|
|
// Write data to flash. At most one block will be processed at a time,
|
478 |
|
|
// but the write may be for a subset of the write. The destination
|
479 |
|
|
// address will be aligned in a way suitable for the bus. The source
|
480 |
|
|
// address need not be aligned. The count is in AM29_TYPE's, i.e.
|
481 |
|
|
// as per the bus alignment, not in bytes.
|
482 |
|
|
static int
|
483 |
|
|
AM29_FNNAME(am29_hw_program)(volatile AM29_TYPE* block_start, volatile AM29_TYPE* addr, const cyg_uint8* buf, cyg_uint32 count, int retries)
|
484 |
|
|
{
|
485 |
|
|
int i;
|
486 |
|
|
|
487 |
|
|
AM29_2RAM_ENTRY_HOOK();
|
488 |
|
|
|
489 |
|
|
for (i = 0; (i < count) && (retries > 0); i++) {
|
490 |
|
|
AM29_TYPE datum, current, active_dq7s;
|
491 |
|
|
|
492 |
|
|
// We can only clear bits, not set them, so any bits that were
|
493 |
|
|
// already clear need to be preserved.
|
494 |
|
|
current = addr[i];
|
495 |
|
|
datum = AM29_NEXT_DATUM(buf) & current;
|
496 |
|
|
if (datum == current) {
|
497 |
|
|
// No change, so just move on.
|
498 |
|
|
continue;
|
499 |
|
|
}
|
500 |
|
|
|
501 |
|
|
block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
502 |
|
|
HAL_MEMORY_BARRIER();
|
503 |
|
|
block_start[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
504 |
|
|
HAL_MEMORY_BARRIER();
|
505 |
|
|
block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_PROGRAM;
|
506 |
|
|
HAL_MEMORY_BARRIER();
|
507 |
|
|
addr[i] = datum;
|
508 |
|
|
HAL_MEMORY_BARRIER();
|
509 |
|
|
|
510 |
|
|
// The data is now being written. The official algorithm is
|
511 |
|
|
// to poll either DQ7 or DQ6, checking DQ5 along the way for
|
512 |
|
|
// error conditions. This gets complicated with parallel
|
513 |
|
|
// flash chips because they may finish at different times.
|
514 |
|
|
// The alternative approach is to ignore the status bits
|
515 |
|
|
// completely and just look for current==datum until the
|
516 |
|
|
// retry count is exceeded. However that does not cope
|
517 |
|
|
// cleanly with cases where the flash chip reports an error
|
518 |
|
|
// early on, e.g. because a flash block is locked.
|
519 |
|
|
|
520 |
|
|
while (--retries > 0) {
|
521 |
|
|
#if CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_DELAY > 0
|
522 |
|
|
// Some chips want a delay between polling
|
523 |
|
|
{ int j; for( j = 0; j < CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_DELAY; j++ ); }
|
524 |
|
|
#endif
|
525 |
|
|
// While the program operation is in progress DQ7 will read
|
526 |
|
|
// back inverted from datum.
|
527 |
|
|
current = addr[i];
|
528 |
|
|
if ((current & AM29_STATUS_DQ7) == (datum & AM29_STATUS_DQ7)) {
|
529 |
|
|
// All DQ7 bits now match datum, so the operation has completed.
|
530 |
|
|
// But not necessarily successfully. On some chips DQ7 may
|
531 |
|
|
// toggle before DQ0-6 are valid, so we need to read the
|
532 |
|
|
// data one more time.
|
533 |
|
|
current = addr[i];
|
534 |
|
|
if (current != datum) {
|
535 |
|
|
retries = 0; // Abort this burst.
|
536 |
|
|
}
|
537 |
|
|
break;
|
538 |
|
|
}
|
539 |
|
|
|
540 |
|
|
// Now we want to check the DQ5 status bits, but only for those
|
541 |
|
|
// chips which are still programming. ((current^datum) & DQ7) gives
|
542 |
|
|
// ones for chips which are still programming, zeroes for chips when
|
543 |
|
|
// the programming is complete.
|
544 |
|
|
active_dq7s = (current ^ datum) & AM29_STATUS_DQ7;
|
545 |
|
|
|
546 |
|
|
if (0 != (current & (active_dq7s >> 2))) {
|
547 |
|
|
// Unfortunately this is not sufficient to prove an error. On
|
548 |
|
|
// some chips DQ0-6 switch to the data while DQ7 is still a
|
549 |
|
|
// status flag, so the set DQ5 bit detected above may be data
|
550 |
|
|
// instead of an error. Check again, this time DQ7 may
|
551 |
|
|
// indicate completion.
|
552 |
|
|
//
|
553 |
|
|
// Next problem. Suppose chip A gave a bogus DQ5 result earlier
|
554 |
|
|
// because it was just finishing. For this read chip A gives
|
555 |
|
|
// back datum, but now chip B is finishing and has reported a
|
556 |
|
|
// bogus DQ5.
|
557 |
|
|
//
|
558 |
|
|
// Solution: if any of the DQ7 lines have changed since the last
|
559 |
|
|
// time, go around the loop again. When an error occurs DQ5
|
560 |
|
|
// remains set and DQ7 remains toggled, so there is no harm
|
561 |
|
|
// in one more polling loop.
|
562 |
|
|
|
563 |
|
|
current = addr[i];
|
564 |
|
|
if (((current ^ datum) & AM29_STATUS_DQ7) != active_dq7s) {
|
565 |
|
|
continue;
|
566 |
|
|
}
|
567 |
|
|
|
568 |
|
|
// DQ5 has been set in a chip where DQ7 indicates an ongoing
|
569 |
|
|
// program operation for two successive reads. That is an error.
|
570 |
|
|
// The hardware is in a strange state so must be reset.
|
571 |
|
|
block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET;
|
572 |
|
|
HAL_MEMORY_BARRIER();
|
573 |
|
|
retries = 0;
|
574 |
|
|
break;
|
575 |
|
|
}
|
576 |
|
|
// No DQ5 bits set in chips which are still programming. Poll again.
|
577 |
|
|
} // Retry for current word
|
578 |
|
|
} // Next word
|
579 |
|
|
|
580 |
|
|
// At this point retries holds the total number of retries left.
|
581 |
|
|
// 0 indicates a timeout or fatal error.
|
582 |
|
|
// >0 indicates success.
|
583 |
|
|
AM29_2RAM_EXIT_HOOK();
|
584 |
|
|
return retries;
|
585 |
|
|
}
|
586 |
|
|
|
587 |
|
|
// FIXME: implement a separate program routine for buffered writes.
|
588 |
|
|
|
589 |
|
|
#if 0
|
590 |
|
|
// Unused for now, but might be useful later
|
591 |
|
|
static int
|
592 |
|
|
AM29_FNNAME(at49_hw_is_locked)(volatile AM29_TYPE* addr)
|
593 |
|
|
{
|
594 |
|
|
int result;
|
595 |
|
|
AM29_TYPE plane;
|
596 |
|
|
|
597 |
|
|
AM29_2RAM_ENTRY_HOOK();
|
598 |
|
|
|
599 |
|
|
// Plane is bits A21-A20 for AT49BV6416
|
600 |
|
|
// A more generic formula is needed.
|
601 |
|
|
plane = AM29_PARALLEL( ((((CYG_ADDRESS)addr)>>21) & 0x3) );
|
602 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
603 |
|
|
HAL_MEMORY_BARRIER();
|
604 |
|
|
addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
605 |
|
|
HAL_MEMORY_BARRIER();
|
606 |
|
|
addr[AM29_OFFSET_COMMAND + plane] = AM29_COMMAND_AUTOSELECT;
|
607 |
|
|
HAL_MEMORY_BARRIER();
|
608 |
|
|
result = addr[AM29_OFFSET_AT49_LOCK_STATUS];
|
609 |
|
|
addr[0] = AM29_COMMAND_RESET;
|
610 |
|
|
HAL_MEMORY_BARRIER();
|
611 |
|
|
// The bottom two bits hold the lock status, LSB indicates
|
612 |
|
|
// soft lock, next bit indicates hard lock. We don't distinguish
|
613 |
|
|
// in this function.
|
614 |
|
|
AM29_2RAM_EXIT_HOOK();
|
615 |
|
|
return (0 != (result & AM29_ID_LOCKED));
|
616 |
|
|
}
|
617 |
|
|
#endif
|
618 |
|
|
|
619 |
|
|
static int
|
620 |
|
|
AM29_FNNAME(at49_hw_softlock)(volatile AM29_TYPE* addr)
|
621 |
|
|
{
|
622 |
|
|
int result = CYG_FLASH_ERR_OK;
|
623 |
|
|
|
624 |
|
|
AM29_2RAM_ENTRY_HOOK();
|
625 |
|
|
|
626 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
627 |
|
|
HAL_MEMORY_BARRIER();
|
628 |
|
|
addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
629 |
|
|
HAL_MEMORY_BARRIER();
|
630 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_AT49_SOFTLOCK_BLOCK_0;
|
631 |
|
|
HAL_MEMORY_BARRIER();
|
632 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
633 |
|
|
HAL_MEMORY_BARRIER();
|
634 |
|
|
addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
635 |
|
|
HAL_MEMORY_BARRIER();
|
636 |
|
|
addr[0] = AM29_COMMAND_AT49_SOFTLOCK_BLOCK_1;
|
637 |
|
|
HAL_MEMORY_BARRIER();
|
638 |
|
|
// not sure if this is required:
|
639 |
|
|
addr[0] = AM29_COMMAND_RESET;
|
640 |
|
|
HAL_MEMORY_BARRIER();
|
641 |
|
|
AM29_2RAM_EXIT_HOOK();
|
642 |
|
|
return result;
|
643 |
|
|
}
|
644 |
|
|
|
645 |
|
|
static int
|
646 |
|
|
AM29_FNNAME(at49_hw_hardlock)(volatile AM29_TYPE* addr)
|
647 |
|
|
{
|
648 |
|
|
int result = CYG_FLASH_ERR_OK;
|
649 |
|
|
|
650 |
|
|
AM29_2RAM_ENTRY_HOOK();
|
651 |
|
|
|
652 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
653 |
|
|
HAL_MEMORY_BARRIER();
|
654 |
|
|
addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
655 |
|
|
HAL_MEMORY_BARRIER();
|
656 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_AT49_HARDLOCK_BLOCK_0;
|
657 |
|
|
HAL_MEMORY_BARRIER();
|
658 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
659 |
|
|
HAL_MEMORY_BARRIER();
|
660 |
|
|
addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2;
|
661 |
|
|
HAL_MEMORY_BARRIER();
|
662 |
|
|
addr[0] = AM29_COMMAND_AT49_HARDLOCK_BLOCK_1;
|
663 |
|
|
HAL_MEMORY_BARRIER();
|
664 |
|
|
// not sure if this is required:
|
665 |
|
|
addr[0] = AM29_COMMAND_RESET;
|
666 |
|
|
HAL_MEMORY_BARRIER();
|
667 |
|
|
AM29_2RAM_EXIT_HOOK();
|
668 |
|
|
return result;
|
669 |
|
|
}
|
670 |
|
|
|
671 |
|
|
static int
|
672 |
|
|
AM29_FNNAME(at49_hw_unlock)(volatile AM29_TYPE* addr)
|
673 |
|
|
{
|
674 |
|
|
int result = CYG_FLASH_ERR_OK;
|
675 |
|
|
|
676 |
|
|
AM29_2RAM_ENTRY_HOOK();
|
677 |
|
|
|
678 |
|
|
addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1;
|
679 |
|
|
HAL_MEMORY_BARRIER();
|
680 |
|
|
addr[0] = AM29_COMMAND_AT49_UNLOCK_BLOCK;
|
681 |
|
|
HAL_MEMORY_BARRIER();
|
682 |
|
|
// not sure if this is required:
|
683 |
|
|
addr[0] = AM29_COMMAND_RESET;
|
684 |
|
|
HAL_MEMORY_BARRIER();
|
685 |
|
|
AM29_2RAM_EXIT_HOOK();
|
686 |
|
|
return result;
|
687 |
|
|
}
|
688 |
|
|
|
689 |
|
|
|
690 |
|
|
// ----------------------------------------------------------------------------
|
691 |
|
|
// Exported code, mostly for placing in a cyg_flash_dev_funs structure.
|
692 |
|
|
|
693 |
|
|
// Just read the device id, either for sanity checking that the system
|
694 |
|
|
// has been configured for the right device, or for filling in the
|
695 |
|
|
// block info by a platform-specific init routine if the platform may
|
696 |
|
|
// be manufactured with one of several different chips.
|
697 |
|
|
int
|
698 |
|
|
AM29_FNNAME(cyg_am29xxxxx_read_devid) (struct cyg_flash_dev* dev)
|
699 |
|
|
{
|
700 |
|
|
int (*query_fn)(volatile AM29_TYPE*);
|
701 |
|
|
int devid;
|
702 |
|
|
volatile AM29_TYPE* addr;
|
703 |
|
|
AM29_INTSCACHE_STATE;
|
704 |
|
|
|
705 |
|
|
CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required");
|
706 |
|
|
|
707 |
|
|
amd_diag("\n");
|
708 |
|
|
|
709 |
|
|
addr = AM29_UNCACHED_ADDRESS(dev->start);
|
710 |
|
|
query_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(am29_hw_query) );
|
711 |
|
|
AM29_INTSCACHE_BEGIN();
|
712 |
|
|
devid = (*query_fn)(addr);
|
713 |
|
|
AM29_INTSCACHE_END();
|
714 |
|
|
return devid;
|
715 |
|
|
}
|
716 |
|
|
|
717 |
|
|
// Validate that the device statically configured is the one on the
|
718 |
|
|
// board.
|
719 |
|
|
int
|
720 |
|
|
AM29_FNNAME(cyg_am29xxxxx_init_check_devid)(struct cyg_flash_dev* dev)
|
721 |
|
|
{
|
722 |
|
|
cyg_am29xxxxx_dev* am29_dev;
|
723 |
|
|
int devid;
|
724 |
|
|
|
725 |
|
|
amd_diag("\n");
|
726 |
|
|
|
727 |
|
|
am29_dev = (cyg_am29xxxxx_dev*) dev->priv;
|
728 |
|
|
devid = AM29_FNNAME(cyg_am29xxxxx_read_devid)(dev);
|
729 |
|
|
if (devid != am29_dev->devid) {
|
730 |
|
|
return CYG_FLASH_ERR_DRV_WRONG_PART;
|
731 |
|
|
}
|
732 |
|
|
|
733 |
|
|
#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME
|
734 |
|
|
{
|
735 |
|
|
volatile AM29_TYPE *addr = AM29_UNCACHED_ADDRESS(dev->start);
|
736 |
|
|
void (*resume_fn)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*);
|
737 |
|
|
resume_fn = (void (*)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*))
|
738 |
|
|
cyg_flash_anonymizer( &AM29_FNNAME(am29_hw_force_all_suspended_resume) );
|
739 |
|
|
AM29_INTSCACHE_STATE;
|
740 |
|
|
|
741 |
|
|
AM29_INTSCACHE_BEGIN();
|
742 |
|
|
(*resume_fn)(dev, am29_dev, addr);
|
743 |
|
|
AM29_INTSCACHE_END();
|
744 |
|
|
}
|
745 |
|
|
#endif
|
746 |
|
|
|
747 |
|
|
// Successfully queried the device, and the id's match. That
|
748 |
|
|
// should be a good enough indication that the flash is working.
|
749 |
|
|
return CYG_FLASH_ERR_OK;
|
750 |
|
|
}
|
751 |
|
|
|
752 |
|
|
// Initialize via a CFI query, instead of statically specifying the
|
753 |
|
|
// boot block layout.
|
754 |
|
|
int
|
755 |
|
|
AM29_FNNAME(cyg_am29xxxxx_init_cfi)(struct cyg_flash_dev* dev)
|
756 |
|
|
{
|
757 |
|
|
int (*cfi_fn)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*);
|
758 |
|
|
volatile AM29_TYPE* addr;
|
759 |
|
|
cyg_am29xxxxx_dev* am29_dev;
|
760 |
|
|
int result;
|
761 |
|
|
AM29_INTSCACHE_STATE;
|
762 |
|
|
|
763 |
|
|
amd_diag("\n");
|
764 |
|
|
|
765 |
|
|
CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required");
|
766 |
|
|
am29_dev = (cyg_am29xxxxx_dev*) dev->priv; // Remove const, only place where this is needed.
|
767 |
|
|
addr = AM29_UNCACHED_ADDRESS(dev->start);
|
768 |
|
|
cfi_fn = (int (*)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*))
|
769 |
|
|
cyg_flash_anonymizer( & AM29_FNNAME(am29_hw_cfi));
|
770 |
|
|
|
771 |
|
|
AM29_INTSCACHE_BEGIN();
|
772 |
|
|
result = (*cfi_fn)(dev, am29_dev, addr);
|
773 |
|
|
AM29_INTSCACHE_END();
|
774 |
|
|
|
775 |
|
|
// Now calculate the device size, and hence the end field.
|
776 |
|
|
if (CYG_FLASH_ERR_OK == result) {
|
777 |
|
|
int i;
|
778 |
|
|
int size = 0;
|
779 |
|
|
for (i = 0; i < dev->num_block_infos; i++) {
|
780 |
|
|
amd_diag("region %d: 0x%08x * %d\n", i, (int)dev->block_info[i].block_size, dev->block_info[i].blocks );
|
781 |
|
|
size += (dev->block_info[i].block_size * dev->block_info[i].blocks);
|
782 |
|
|
}
|
783 |
|
|
dev->end = dev->start + size - 1;
|
784 |
|
|
|
785 |
|
|
#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME
|
786 |
|
|
{
|
787 |
|
|
void (*resume_fn)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*);
|
788 |
|
|
resume_fn = (void (*)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*))
|
789 |
|
|
cyg_flash_anonymizer( &AM29_FNNAME(am29_hw_force_all_suspended_resume) );
|
790 |
|
|
|
791 |
|
|
AM29_INTSCACHE_BEGIN();
|
792 |
|
|
(*resume_fn)(dev, am29_dev, addr);
|
793 |
|
|
AM29_INTSCACHE_END();
|
794 |
|
|
}
|
795 |
|
|
#endif
|
796 |
|
|
}
|
797 |
|
|
return result;
|
798 |
|
|
}
|
799 |
|
|
|
800 |
|
|
// Erase a single block. The calling code will have supplied a pointer
|
801 |
|
|
// aligned to a block boundary.
|
802 |
|
|
int
|
803 |
|
|
AM29_FNNAME(cyg_am29xxxxx_erase)(struct cyg_flash_dev* dev, cyg_flashaddr_t addr)
|
804 |
|
|
{
|
805 |
|
|
int (*erase_fn)(volatile AM29_TYPE*);
|
806 |
|
|
volatile AM29_TYPE* block;
|
807 |
|
|
cyg_flashaddr_t block_start;
|
808 |
|
|
size_t block_size;
|
809 |
|
|
int i;
|
810 |
|
|
int result;
|
811 |
|
|
AM29_INTSCACHE_STATE;
|
812 |
|
|
|
813 |
|
|
CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required");
|
814 |
|
|
CYG_ASSERT((addr >= dev->start) && (addr <= dev->end), "flash address out of device range");
|
815 |
|
|
|
816 |
|
|
am29_get_block_info(dev, addr, &block_start, &block_size);
|
817 |
|
|
CYG_ASSERT(addr == block_start, "erase address should be the start of a flash block");
|
818 |
|
|
|
819 |
|
|
amd_diag("addr %p block %p[%d]\n", (void*)addr, (void*)block_start, (int)block_size );
|
820 |
|
|
|
821 |
|
|
block = AM29_UNCACHED_ADDRESS(addr);
|
822 |
|
|
erase_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(am29_hw_erase) );
|
823 |
|
|
|
824 |
|
|
AM29_INTSCACHE_BEGIN();
|
825 |
|
|
result = (*erase_fn)(block);
|
826 |
|
|
AM29_INTSCACHE_END();
|
827 |
|
|
|
828 |
|
|
// The erase may have failed for a number of reasons, e.g. because
|
829 |
|
|
// of a locked sector. The best thing to do here is to check that the
|
830 |
|
|
// erase has succeeded.
|
831 |
|
|
block = (AM29_TYPE*) addr;
|
832 |
|
|
for (i = 0; i < (block_size / sizeof(AM29_TYPE)); i++) {
|
833 |
|
|
if (block[i] != (AM29_TYPE)~0) {
|
834 |
|
|
// There is no easy way of detecting the specific error,
|
835 |
|
|
// e.g. locked flash block, timeout, ... So return a
|
836 |
|
|
// useless catch-all error.
|
837 |
|
|
return CYG_FLASH_ERR_ERASE;
|
838 |
|
|
}
|
839 |
|
|
}
|
840 |
|
|
return CYG_FLASH_ERR_OK;
|
841 |
|
|
}
|
842 |
|
|
|
843 |
|
|
// Write some data to the flash. The destination must be aligned
|
844 |
|
|
// appropriately for the bus width (not the device width).
|
845 |
|
|
int
|
846 |
|
|
AM29_FNNAME(cyg_am29xxxxx_program)(struct cyg_flash_dev* dev, cyg_flashaddr_t dest, const void* src, size_t len)
|
847 |
|
|
{
|
848 |
|
|
int (*program_fn)(volatile AM29_TYPE*, volatile AM29_TYPE*, const cyg_uint8*, cyg_uint32, int);
|
849 |
|
|
volatile AM29_TYPE* block;
|
850 |
|
|
volatile AM29_TYPE* addr;
|
851 |
|
|
cyg_flashaddr_t block_start;
|
852 |
|
|
size_t block_size;
|
853 |
|
|
const cyg_uint8* data;
|
854 |
|
|
int retries;
|
855 |
|
|
int i;
|
856 |
|
|
|
857 |
|
|
AM29_INTSCACHE_STATE;
|
858 |
|
|
|
859 |
|
|
CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required");
|
860 |
|
|
|
861 |
|
|
amd_diag("dest %p src %p len %d\n", (void*)dest, (void*)src, (int)len );
|
862 |
|
|
|
863 |
|
|
// Only support writes that are aligned to the bus boundary. This
|
864 |
|
|
// may be more restrictive than what the hardware is capable of.
|
865 |
|
|
// However it ensures that the hw_program routine can write as
|
866 |
|
|
// much data as possible each iteration, and hence significantly
|
867 |
|
|
// improves performance. The length had better be a multiple of
|
868 |
|
|
// the bus width as well
|
869 |
|
|
if ((0 != ((CYG_ADDRWORD)dest & (sizeof(AM29_TYPE) - 1))) ||
|
870 |
|
|
(0 != (len & (sizeof(AM29_TYPE) - 1)))) {
|
871 |
|
|
return CYG_FLASH_ERR_INVALID;
|
872 |
|
|
}
|
873 |
|
|
|
874 |
|
|
addr = AM29_UNCACHED_ADDRESS(dest);
|
875 |
|
|
CYG_ASSERT((dest >= dev->start) && (dest <= dev->end), "flash address out of device range");
|
876 |
|
|
|
877 |
|
|
am29_get_block_info(dev, dest, &block_start, &block_size);
|
878 |
|
|
CYG_ASSERT(((dest - block_start) + len) <= block_size, "write cannot cross block boundary");
|
879 |
|
|
|
880 |
|
|
block = AM29_UNCACHED_ADDRESS(block_start);
|
881 |
|
|
data = (const cyg_uint8*) src;
|
882 |
|
|
retries = CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_TIMEOUT;
|
883 |
|
|
|
884 |
|
|
program_fn = (int (*)(volatile AM29_TYPE*, volatile AM29_TYPE*, const cyg_uint8*, cyg_uint32, int))
|
885 |
|
|
cyg_flash_anonymizer( & AM29_FNNAME(am29_hw_program) );
|
886 |
|
|
|
887 |
|
|
AM29_INTSCACHE_BEGIN();
|
888 |
|
|
(*program_fn)(block, addr, data, len / sizeof(AM29_TYPE), retries);
|
889 |
|
|
AM29_INTSCACHE_END();
|
890 |
|
|
|
891 |
|
|
// Too many things can go wrong when manipulating the h/w, so
|
892 |
|
|
// verify the operation by actually checking the data.
|
893 |
|
|
addr = (volatile AM29_TYPE*) dest;
|
894 |
|
|
data = (const cyg_uint8*) src;
|
895 |
|
|
for (i = 0; i < (len / sizeof(AM29_TYPE)); i++) {
|
896 |
|
|
AM29_TYPE datum = AM29_NEXT_DATUM(data);
|
897 |
|
|
AM29_TYPE current = addr[i];
|
898 |
|
|
if ((datum & current) != current) {
|
899 |
|
|
amd_diag("data %p addr[i] %p datum %08x current %08x\n", data-sizeof(AM29_TYPE), &addr[i], datum, current );
|
900 |
|
|
return CYG_FLASH_ERR_PROGRAM;
|
901 |
|
|
}
|
902 |
|
|
}
|
903 |
|
|
return CYG_FLASH_ERR_OK;
|
904 |
|
|
}
|
905 |
|
|
|
906 |
|
|
int
|
907 |
|
|
AM29_FNNAME(cyg_at49xxxx_softlock)(struct cyg_flash_dev* dev, const cyg_flashaddr_t dest)
|
908 |
|
|
{
|
909 |
|
|
volatile AM29_TYPE* uncached;
|
910 |
|
|
int result;
|
911 |
|
|
int (*lock_fn)(volatile AM29_TYPE*);
|
912 |
|
|
AM29_INTSCACHE_STATE;
|
913 |
|
|
|
914 |
|
|
amd_diag("\n");
|
915 |
|
|
|
916 |
|
|
CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required");
|
917 |
|
|
CYG_ASSERT((dest >= (cyg_flashaddr_t)dev->start) &&
|
918 |
|
|
((CYG_ADDRESS)dest <= dev->end), "flash address out of device range");
|
919 |
|
|
|
920 |
|
|
uncached = AM29_UNCACHED_ADDRESS(dest);
|
921 |
|
|
lock_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(at49_hw_softlock) );
|
922 |
|
|
AM29_INTSCACHE_BEGIN();
|
923 |
|
|
result = (*lock_fn)(uncached);
|
924 |
|
|
AM29_INTSCACHE_END();
|
925 |
|
|
return result;
|
926 |
|
|
}
|
927 |
|
|
|
928 |
|
|
int
|
929 |
|
|
AM29_FNNAME(cyg_at49xxxx_hardlock)(struct cyg_flash_dev* dev, const cyg_flashaddr_t dest)
|
930 |
|
|
{
|
931 |
|
|
volatile AM29_TYPE* uncached;
|
932 |
|
|
int result;
|
933 |
|
|
int (*lock_fn)(volatile AM29_TYPE*);
|
934 |
|
|
AM29_INTSCACHE_STATE;
|
935 |
|
|
|
936 |
|
|
amd_diag("\n");
|
937 |
|
|
|
938 |
|
|
CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required");
|
939 |
|
|
CYG_ASSERT((dest >= (cyg_flashaddr_t)dev->start) &&
|
940 |
|
|
((CYG_ADDRESS)dest <= dev->end), "flash address out of device range");
|
941 |
|
|
|
942 |
|
|
uncached = AM29_UNCACHED_ADDRESS(dest);
|
943 |
|
|
lock_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(at49_hw_hardlock) );
|
944 |
|
|
AM29_INTSCACHE_BEGIN();
|
945 |
|
|
result = (*lock_fn)(uncached);
|
946 |
|
|
AM29_INTSCACHE_END();
|
947 |
|
|
return result;
|
948 |
|
|
}
|
949 |
|
|
|
950 |
|
|
int
|
951 |
|
|
AM29_FNNAME(cyg_at49xxxx_unlock)(struct cyg_flash_dev* dev, const cyg_flashaddr_t dest)
|
952 |
|
|
{
|
953 |
|
|
volatile AM29_TYPE* uncached;
|
954 |
|
|
int result;
|
955 |
|
|
int (*unlock_fn)(volatile AM29_TYPE*);
|
956 |
|
|
AM29_INTSCACHE_STATE;
|
957 |
|
|
|
958 |
|
|
amd_diag("\n");
|
959 |
|
|
|
960 |
|
|
CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required");
|
961 |
|
|
CYG_ASSERT((dest >= (cyg_flashaddr_t)dev->start) &&
|
962 |
|
|
((CYG_ADDRESS)dest <= dev->end), "flash address out of device range");
|
963 |
|
|
|
964 |
|
|
uncached = AM29_UNCACHED_ADDRESS(dest);
|
965 |
|
|
unlock_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(at49_hw_unlock) );
|
966 |
|
|
|
967 |
|
|
AM29_INTSCACHE_BEGIN();
|
968 |
|
|
result = (*unlock_fn)(uncached);
|
969 |
|
|
AM29_INTSCACHE_END();
|
970 |
|
|
return result;
|
971 |
|
|
}
|
972 |
|
|
|
973 |
|
|
// ----------------------------------------------------------------------------
|
974 |
|
|
// Clean up the various #define's so this file can be #include'd again
|
975 |
|
|
#undef AM29_FNNAME
|
976 |
|
|
#undef AM29_RAMFNDECL
|
977 |
|
|
#undef AM29_OFFSET_COMMAND
|
978 |
|
|
#undef AM29_OFFSET_COMMAND2
|
979 |
|
|
#undef AM29_OFFSET_DEVID
|
980 |
|
|
#undef AM29_OFFSET_DEVID2
|
981 |
|
|
#undef AM29_OFFSET_DEVID3
|
982 |
|
|
#undef AM29_OFFSET_CFI
|
983 |
|
|
#undef AM29_OFFSET_CFI_DATA
|
984 |
|
|
#undef AM29_OFFSET_AT49_LOCK_STATUS
|
985 |
|
|
#undef AM29_PARALLEL
|