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