Remove FSF address from GPL notices
[fw/openocd] / src / flash / nor / aducm360.c
1 /***************************************************************************
2  *   Copyright (C) 2015 by Ivan Buliev                                     *
3  *   i.buliev@mikrosistemi.com                                             *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
17  ***************************************************************************/
18
19 /***************************************************************************
20  *  This version for ADuCM360 is largely based on the following flash      *
21  *  drivers:                                                               *
22  *    - aduc702x.c                                                         *
23  *          Copyright (C) 2008 by Kevin McGuire                            *
24  *          Copyright (C) 2008 by Marcel Wijlaars                          *
25  *          Copyright (C) 2009 by Michael Ashton                           *
26  *   and                                                                   *
27  *    - stm32f1x.c                                                         *
28  *          Copyright (C) 2005 by Dominic Rath                             *
29  *          Dominic.Rath@gmx.de                                            *
30  *                                                                         *
31  *          Copyright (C) 2008 by Spencer Oliver                           *
32  *          spen@spen-soft.co.uk                                           *
33  *                                                                         *
34  *          Copyright (C) 2011 by Andreas Fritiofson                       *
35  *          andreas.fritiofson@gmail.com                                   *
36  ***************************************************************************/
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include "imp.h"
43 #include <helper/binarybuffer.h>
44 #include <helper/time_support.h>
45 #include <target/algorithm.h>
46 #include <target/armv7m.h>
47
48 static int aducm360_build_sector_list(struct flash_bank *bank);
49 static int aducm360_check_flash_completion(struct target *target, unsigned int timeout_ms);
50 static int aducm360_set_write_enable(struct target *target, int enable);
51
52 #define ADUCM360_FLASH_BASE             0x40002800
53 #define ADUCM360_FLASH_FEESTA           0x0000
54 #define ADUCM360_FLASH_FEECON0          0x0004
55 #define ADUCM360_FLASH_FEECMD           0x0008
56 #define ADUCM360_FLASH_FEEADR0L         0x0010
57 #define ADUCM360_FLASH_FEEADR0H         0x0014
58 #define ADUCM360_FLASH_FEEADR1L         0x0018
59 #define ADUCM360_FLASH_FEEADR1H         0x001C
60 #define ADUCM360_FLASH_FEEKEY           0x0020
61 #define ADUCM360_FLASH_FEEPROL          0x0028
62 #define ADUCM360_FLASH_FEEPROH          0x002C
63 #define ADUCM360_FLASH_FEESIGL          0x0030
64 #define ADUCM360_FLASH_FEESIGH          0x0034
65 #define ADUCM360_FLASH_FEECON1          0x0038
66 #define ADUCM360_FLASH_FEEADRAL         0x0048
67 #define ADUCM360_FLASH_FEEADRAH         0x004C
68 #define ADUCM360_FLASH_FEEAEN0          0x0078
69 #define ADUCM360_FLASH_FEEAEN1          0x007C
70 #define ADUCM360_FLASH_FEEAEN2          0x0080
71
72 /* flash bank aducm360 0 0 0 0 <target#> */
73 FLASH_BANK_COMMAND_HANDLER(aducm360_flash_bank_command)
74 {
75         bank->base = 0x00000000;
76         bank->size = 0x00020000;
77
78         aducm360_build_sector_list(bank);
79
80         return ERROR_OK;
81 }
82
83 #define FLASH_SECTOR_SIZE       512
84
85 /* ----------------------------------------------------------------------- */
86 static int aducm360_build_sector_list(struct flash_bank *bank)
87 {
88         int i = 0;
89         uint32_t offset = 0;
90
91         /* sector size is 512 */
92         bank->num_sectors = bank->size / FLASH_SECTOR_SIZE;
93         bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
94         for (i = 0; i < bank->num_sectors; ++i) {
95                 bank->sectors[i].offset = offset;
96                 bank->sectors[i].size = FLASH_SECTOR_SIZE;
97                 offset += bank->sectors[i].size;
98                 bank->sectors[i].is_erased = -1;
99                 bank->sectors[i].is_protected = 0;
100         }
101
102         return ERROR_OK;
103 }
104
105 /* ----------------------------------------------------------------------- */
106 static int aducm360_protect_check(struct flash_bank *bank)
107 {
108         LOG_WARNING("aducm360_protect_check not implemented.");
109         return ERROR_OK;
110 }
111
112 /* ----------------------------------------------------------------------- */
113 static int aducm360_mass_erase(struct target *target)
114 {
115         uint32_t                value;
116         int                             res = ERROR_OK;
117
118         /* Clear any old status */
119         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
120
121         /* Enable the writing to the flash*/
122         aducm360_set_write_enable(target, 1);
123
124         /* Unlock for writing */
125         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F456);
126         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F123);
127         /* Issue the 'MASSERASE' command */
128         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEECMD, 0x00000003);
129
130         /* Check the result */
131         res = aducm360_check_flash_completion(target, 3500);
132         if (res != ERROR_OK) {
133                 LOG_ERROR("mass erase failed.");
134                 aducm360_set_write_enable(target, 0);
135                 res = ERROR_FLASH_OPERATION_FAILED;
136         }
137
138         return res;
139 }
140
141 /* ----------------------------------------------------------------------- */
142 static int aducm360_page_erase(struct target *target, uint32_t padd)
143 {
144         uint32_t                value;
145         int                             res = ERROR_OK;
146
147         /* Clear any old status */
148         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
149
150         /* Enable the writing to the flash*/
151         aducm360_set_write_enable(target, 1);
152
153         /* Unlock for writing */
154         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F456);
155         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F123);
156         /* Write the sector address */
157         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEADR0L, padd & 0xFFFF);
158         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEADR0H, (padd>>16) & 0xFFFF);
159         /* Issue the 'ERASEPAGE' command */
160         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEECMD, 0x00000001);
161
162         /* Check the result */
163         res = aducm360_check_flash_completion(target, 50);
164         if (res != ERROR_OK) {
165                 LOG_ERROR("page erase failed at 0x%08" PRIx32, padd);
166                 aducm360_set_write_enable(target, 0);
167                 res = ERROR_FLASH_OPERATION_FAILED;
168         }
169
170         return res;
171 }
172
173 /* ----------------------------------------------------------------------- */
174 static int aducm360_erase(struct flash_bank *bank, int first, int last)
175 {
176         int             res = ERROR_OK;
177         int             i;
178         int             count;
179         struct target   *target = bank->target;
180         uint32_t        padd;
181
182         if (((first | last) == 0) || ((first == 0) && (last >= bank->num_sectors))) {
183                 res = aducm360_mass_erase(target);
184         } else {
185                 count = last - first + 1;
186                 for (i = 0; i < count; ++i) {
187                         padd = bank->base + ((first+i)*FLASH_SECTOR_SIZE);
188                         res = aducm360_page_erase(target, padd);
189                         if (res != ERROR_OK)
190                                 break;
191                 }
192         }
193
194         return res;
195 }
196
197 /* ----------------------------------------------------------------------- */
198 static int aducm360_protect(struct flash_bank *bank, int set, int first, int last)
199 {
200         LOG_ERROR("aducm360_protect not implemented.");
201         return ERROR_FLASH_OPERATION_FAILED;
202 }
203
204 /* ----------------------------------------------------------------------- */
205 static int aducm360_write_block_sync(
206                 struct flash_bank *bank,
207                 const uint8_t *buffer,
208                 uint32_t offset,
209                 uint32_t count)
210 {
211         struct target           *target = bank->target;
212         uint32_t                target_buffer_size = 8192;
213         struct working_area     *helper;
214         struct working_area     *target_buffer;
215         uint32_t                address = bank->base + offset;
216         struct reg_param        reg_params[8];
217         int                     retval = ERROR_OK;
218         uint32_t                entry_point = 0, exit_point = 0;
219         uint32_t                res;
220         struct armv7m_algorithm armv7m_algo;
221
222         static const uint32_t aducm360_flash_write_code[] = {
223                         /* helper.code */
224                         0x88AF4D10, 0x0704F047, 0x682F80AF, 0x600E6806,
225                         0xF017882F, 0xF43F0F08, 0xF851AFFB, 0x42B77B04,
226                         0x800DF040, 0x0004F100, 0xF47F3A04, 0x686FAFEF,
227                         0x0704F027, 0xF04F80AF, 0xF0000400, 0xF04FB802,
228                         0xBE000480, 0x40002800, 0x00015000, 0x20000000,
229                         0x00013000
230         };
231
232         LOG_DEBUG("'aducm360_write_block_sync' requested, dst:0x%08" PRIx32 ", count:0x%08" PRIx32 "bytes.",
233                         address, count);
234
235         /*  ----- Check the destination area for a Long Word alignment -----  */
236         if (((count%4) != 0) || ((offset%4) != 0)) {
237                 LOG_ERROR("write block must be multiple of four bytes in offset & length");
238                 return ERROR_FAIL;
239         }
240
241         /*  ----- Allocate space in the target's RAM for the helper code -----  */
242         if (target_alloc_working_area(target, sizeof(aducm360_flash_write_code),
243                         &helper) != ERROR_OK) {
244                 LOG_WARNING("no working area available, can't do block memory writes");
245                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
246         }
247
248         /*  ----- Upload the helper code to the space in the target's RAM -----  */
249         uint8_t code[sizeof(aducm360_flash_write_code)];
250         target_buffer_set_u32_array(target, code, ARRAY_SIZE(aducm360_flash_write_code),
251                         aducm360_flash_write_code);
252         retval = target_write_buffer(target, helper->address, sizeof(code), code);
253         if (retval != ERROR_OK)
254                 return retval;
255         entry_point = helper->address;
256
257         /*  ----- Allocate space in the target's RAM for the user application's object code -----  */
258         while (target_alloc_working_area_try(target, target_buffer_size, &target_buffer) != ERROR_OK) {
259                 LOG_WARNING("couldn't allocate a buffer space of 0x%08" PRIx32 "bytes in the target's SRAM.",
260                                 target_buffer_size);
261                 target_buffer_size /= 2;
262                 if (target_buffer_size <= 256) {                /* No room available */
263                         LOG_WARNING("no large enough working area available, can't do block memory writes");
264                         target_free_working_area(target, helper);
265                         return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
266                 }
267         }
268
269         /* ----- Prepare the target for the helper ----- */
270         armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
271         armv7m_algo.core_mode = ARM_MODE_THREAD;
272
273         init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT); /*SRC      */
274         init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /*DST      */
275         init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /*COUNT    */
276         init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /*not used */
277         init_reg_param(&reg_params[4], "r4", 32, PARAM_IN);      /*RESULT   */
278
279         /*  ===== Execute the Main Programming Loop! ===== */
280         while (count > 0) {
281                 uint32_t thisrun_count = (count > target_buffer_size) ? target_buffer_size : count;
282
283                 /* ----- Upload the chunk ----- */
284                 retval = target_write_buffer(target, target_buffer->address, thisrun_count, buffer);
285                 if (retval != ERROR_OK)
286                         break;
287                 /* Set the arguments for the helper */
288                 buf_set_u32(reg_params[0].value, 0, 32, target_buffer->address);        /*SRC     */
289                 buf_set_u32(reg_params[1].value, 0, 32, address);                                       /*DST     */
290                 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);                         /*COUNT   */
291                 buf_set_u32(reg_params[3].value, 0, 32, 0);                                                     /*NOT USED*/
292
293                 retval = target_run_algorithm(target, 0, NULL, 5,
294                                 reg_params,     entry_point, exit_point, 10000, &armv7m_algo);
295                 if (retval != ERROR_OK) {
296                         LOG_ERROR("error executing aducm360 flash write algorithm");
297                         break;
298                 }
299
300                 res = buf_get_u32(reg_params[4].value, 0, 32);
301                 if (res) {
302                         LOG_ERROR("aducm360 fast sync algorithm reports an error (%02X)", res);
303                         retval = ERROR_FAIL;
304                         break;
305                 }
306
307                 buffer += thisrun_count;
308                 address += thisrun_count;
309                 count -= thisrun_count;
310         }
311
312         target_free_working_area(target, target_buffer);
313         target_free_working_area(target, helper);
314
315         destroy_reg_param(&reg_params[0]);
316         destroy_reg_param(&reg_params[1]);
317         destroy_reg_param(&reg_params[2]);
318         destroy_reg_param(&reg_params[3]);
319         destroy_reg_param(&reg_params[4]);
320
321         return retval;
322 }
323
324 /* ----------------------------------------------------------------------- */
325 static int aducm360_write_block_async(
326                 struct flash_bank *bank,
327                 const uint8_t *buffer,
328                 uint32_t offset,
329                 uint32_t count)
330 {
331         struct target           *target = bank->target;
332         uint32_t                target_buffer_size = 1024;
333         struct working_area     *helper;
334         struct working_area     *target_buffer;
335         uint32_t                address = bank->base + offset;
336         struct reg_param        reg_params[9];
337         int                     retval = ERROR_OK;
338         uint32_t                entry_point = 0, exit_point = 0;
339         uint32_t                res;
340         uint32_t                wcount;
341         struct armv7m_algorithm armv7m_algo;
342
343         static const uint32_t aducm360_flash_write_code[] = {
344                         /* helper.code */
345                         0x4050F8DF,     0xF04588A5,     0x80A50504,     0x8000F8D0,
346                         0x0F00F1B8, 0x8016F000, 0x45476847,     0xAFF6F43F,
347                         0x6B04F857, 0x6B04F842, 0xF0158825,     0xF43F0F08,
348                         0x428FAFFB, 0xF100BF28, 0x60470708,     0xB10B3B01,
349                         0xBFE4F7FF, 0xF02588A5, 0x80A50504,     0x0900F04F,
350                         0xBE00BF00, 0x40002800, 0x20000000,     0x20000100,
351                         0x00013000
352         };
353
354         LOG_DEBUG("'aducm360_write_block_async' requested, dst:0x%08" PRIx32 ", count:0x%08" PRIx32 "bytes.",
355                         address, count);
356
357         /*  ----- Check the destination area for a Long Word alignment -----  */
358         if (((count%4) != 0) || ((offset%4) != 0)) {
359                 LOG_ERROR("write block must be multiple of four bytes in offset & length");
360                 return ERROR_FAIL;
361         }
362         wcount = count/4;
363
364         /*  ----- Allocate space in the target's RAM for the helper code -----  */
365         if (target_alloc_working_area(target, sizeof(aducm360_flash_write_code),
366                         &helper) != ERROR_OK) {
367                 LOG_WARNING("no working area available, can't do block memory writes");
368                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
369         }
370
371         /*  ----- Upload the helper code to the space in the target's RAM -----  */
372         uint8_t code[sizeof(aducm360_flash_write_code)];
373         target_buffer_set_u32_array(target, code, ARRAY_SIZE(aducm360_flash_write_code),
374                         aducm360_flash_write_code);
375         retval = target_write_buffer(target, helper->address, sizeof(code), code);
376         if (retval != ERROR_OK)
377                 return retval;
378         entry_point = helper->address;
379
380         /*  ----- Allocate space in the target's RAM for the user application's object code ----- */
381         while (target_alloc_working_area_try(target, target_buffer_size, &target_buffer) != ERROR_OK) {
382                 LOG_WARNING("couldn't allocate a buffer space of 0x%08" PRIx32 "bytes in the target's SRAM.",
383                                 target_buffer_size);
384                 target_buffer_size /= 2;
385                 if (target_buffer_size <= 256) {                /* No room available */
386                         LOG_WARNING("no large enough working area available, can't do block memory writes");
387                         target_free_working_area(target, helper);
388                         return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
389                 }
390         }
391
392         /* ----- Prepare the target for the helper ----- */
393         armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
394         armv7m_algo.core_mode = ARM_MODE_THREAD;
395
396         init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT); /*SRCBEG     */
397         init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /*SRCEND     */
398         init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /*DST        */
399         init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /*COUNT (LWs)*/
400         init_reg_param(&reg_params[4], "r9", 32, PARAM_IN);  /*RESULT     */
401
402         buf_set_u32(reg_params[0].value, 0, 32, target_buffer->address);
403         buf_set_u32(reg_params[1].value, 0, 32, target_buffer->address + target_buffer->size);
404         buf_set_u32(reg_params[2].value, 0, 32, address);
405         buf_set_u32(reg_params[3].value, 0, 32, wcount);
406
407         retval = target_run_flash_async_algorithm(target, buffer, wcount, 4,
408                         0, NULL,
409                         5, reg_params,
410                         target_buffer->address, target_buffer->size,
411                         entry_point, exit_point,
412                         &armv7m_algo);
413         if (retval != ERROR_OK) {
414                 LOG_ERROR("error executing aducm360 flash write algorithm");
415         } else {
416                 res = buf_get_u32(reg_params[4].value, 0, 32);  /*RESULT*/
417                 if (res) {
418                         LOG_ERROR("aducm360 fast async algorithm reports an error (%02X)", res);
419                         retval = ERROR_FAIL;
420                 }
421         }
422
423         target_free_working_area(target, target_buffer);
424         target_free_working_area(target, helper);
425
426         destroy_reg_param(&reg_params[0]);
427         destroy_reg_param(&reg_params[1]);
428         destroy_reg_param(&reg_params[2]);
429         destroy_reg_param(&reg_params[3]);
430         destroy_reg_param(&reg_params[4]);
431
432         return retval;
433 }
434
435 /* ----------------------------------------------------------------------- */
436 /* If this fn returns ERROR_TARGET_RESOURCE_NOT_AVAILABLE, then the caller can fall
437  * back to another mechanism that does not require onboard RAM
438  *
439  * Caller should not check for other return values specifically
440  */
441 static int aducm360_write_block(struct flash_bank *bank,
442         const uint8_t *buffer,
443         uint32_t offset,
444         uint32_t count)
445 {
446         int     choice = 0;
447
448         switch (choice) {
449         case 0:
450                 return aducm360_write_block_sync(bank, buffer, offset, count);
451                 break;
452         case 1:
453                 return aducm360_write_block_async(bank, buffer, offset, count);
454                 break;
455         default:
456                 LOG_ERROR("aducm360_write_block was cancelled (no writing method was chosen)!");
457                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
458         }
459 }
460
461 /* ----------------------------------------------------------------------- */
462 #define FEESTA_WRDONE   0x00000008
463
464 static int aducm360_write_modified(struct flash_bank *bank,
465                 const uint8_t *buffer,
466                 uint32_t offset,
467                 uint32_t count)
468 {
469         uint32_t                value;
470         int                             res = ERROR_OK;
471         uint32_t        i, j, a, d;
472         struct target   *target = bank->target;
473
474         LOG_DEBUG("performing slow write (offset=0x%08" PRIx32 ", count=0x%08" PRIx32 ")...",
475                         offset, count);
476
477         /* Enable the writing to the flash */
478         aducm360_set_write_enable(target, 1);
479
480         /* Clear any old status */
481         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
482
483         for (i = 0; i < count; i += 4) {
484                 a = offset+i;
485                 for (j = 0; i < 4; i += 1)
486                         *((uint8_t *)(&d) + j) = buffer[i+j];
487                 target_write_u32(target, a, d);
488                 do {
489                         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
490                 } while (!(value & FEESTA_WRDONE));
491         }
492         aducm360_set_write_enable(target, 0);
493
494         return res;
495 }
496
497 /* ----------------------------------------------------------------------- */
498 static int aducm360_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
499 {
500         int retval;
501
502         /* try using a block write */
503         retval = aducm360_write_block(bank, buffer, offset, count);
504         if (retval != ERROR_OK) {
505                 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
506                         /* if block write failed (no sufficient working area),
507                          * use normal (slow) JTAG method */
508                         LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
509
510                         retval = aducm360_write_modified(bank, buffer, offset, count);
511                         if (retval != ERROR_OK) {
512                                 LOG_ERROR("slow write failed");
513                                 return ERROR_FLASH_OPERATION_FAILED;
514                         }
515                 }
516         }
517         return retval;
518 }
519
520 /* ----------------------------------------------------------------------- */
521 static int aducm360_probe(struct flash_bank *bank)
522 {
523         return ERROR_OK;
524 }
525
526 /* ----------------------------------------------------------------------- */
527 /* sets FEECON0 bit 2
528  * enable = 1 enables writes & erases, 0 disables them */
529 static int aducm360_set_write_enable(struct target *target, int enable)
530 {
531         /* don't bother to preserve int enable bit here */
532         uint32_t        value;
533
534         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEECON0, &value);
535         if (enable)
536                 value |= 0x00000004;
537         else
538                 value &= ~0x00000004;
539         target_write_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEECON0, value);
540
541         return ERROR_OK;
542 }
543
544 /* ----------------------------------------------------------------------- */
545 /* wait up to timeout_ms for controller to not be busy,
546  * then check whether the command passed or failed.
547  *
548  * this function sleeps 1ms between checks (after the first one),
549  * so in some cases may slow things down without a usleep after the first read */
550 static int aducm360_check_flash_completion(struct target *target, unsigned int timeout_ms)
551 {
552         uint32_t v = 1;
553
554         long long endtime = timeval_ms() + timeout_ms;
555         while (1) {
556                 target_read_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEESTA, &v);
557                 if ((v & 0x00000001) == 0)
558                         break;
559                 alive_sleep(1);
560                 if (timeval_ms() >= endtime)
561                         break;
562         }
563
564         if (!(v & 0x00000004))  /* b2 */
565                 return ERROR_FAIL;
566
567         return ERROR_OK;
568 }
569
570 /* ----------------------------------------------------------------------- */
571 struct flash_driver aducm360_flash = {
572         .name = "aducm360",
573         .flash_bank_command = aducm360_flash_bank_command,
574         .erase = aducm360_erase,
575         .protect = aducm360_protect,
576         .write = aducm360_write,
577         .read = default_flash_read,
578         .probe = aducm360_probe,
579         .auto_probe = aducm360_probe,
580         .erase_check = default_flash_blank_check,
581         .protect_check = aducm360_protect_check,
582 };