Constify struct flash_driver instances
[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_mass_erase(struct target *target)
107 {
108         uint32_t                value;
109         int                             res = ERROR_OK;
110
111         /* Clear any old status */
112         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
113
114         /* Enable the writing to the flash*/
115         aducm360_set_write_enable(target, 1);
116
117         /* Unlock for writing */
118         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F456);
119         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F123);
120         /* Issue the 'MASSERASE' command */
121         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEECMD, 0x00000003);
122
123         /* Check the result */
124         res = aducm360_check_flash_completion(target, 3500);
125         if (res != ERROR_OK) {
126                 LOG_ERROR("mass erase failed.");
127                 aducm360_set_write_enable(target, 0);
128                 res = ERROR_FLASH_OPERATION_FAILED;
129         }
130
131         return res;
132 }
133
134 /* ----------------------------------------------------------------------- */
135 static int aducm360_page_erase(struct target *target, uint32_t padd)
136 {
137         uint32_t                value;
138         int                             res = ERROR_OK;
139
140         /* Clear any old status */
141         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
142
143         /* Enable the writing to the flash*/
144         aducm360_set_write_enable(target, 1);
145
146         /* Unlock for writing */
147         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F456);
148         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F123);
149         /* Write the sector address */
150         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEADR0L, padd & 0xFFFF);
151         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEADR0H, (padd>>16) & 0xFFFF);
152         /* Issue the 'ERASEPAGE' command */
153         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEECMD, 0x00000001);
154
155         /* Check the result */
156         res = aducm360_check_flash_completion(target, 50);
157         if (res != ERROR_OK) {
158                 LOG_ERROR("page erase failed at 0x%08" PRIx32, padd);
159                 aducm360_set_write_enable(target, 0);
160                 res = ERROR_FLASH_OPERATION_FAILED;
161         }
162
163         return res;
164 }
165
166 /* ----------------------------------------------------------------------- */
167 static int aducm360_erase(struct flash_bank *bank, int first, int last)
168 {
169         int             res = ERROR_OK;
170         int             i;
171         int             count;
172         struct target   *target = bank->target;
173         uint32_t        padd;
174
175         if (((first | last) == 0) || ((first == 0) && (last >= bank->num_sectors))) {
176                 res = aducm360_mass_erase(target);
177         } else {
178                 count = last - first + 1;
179                 for (i = 0; i < count; ++i) {
180                         padd = bank->base + ((first+i)*FLASH_SECTOR_SIZE);
181                         res = aducm360_page_erase(target, padd);
182                         if (res != ERROR_OK)
183                                 break;
184                 }
185         }
186
187         return res;
188 }
189
190 /* ----------------------------------------------------------------------- */
191 static int aducm360_write_block_sync(
192                 struct flash_bank *bank,
193                 const uint8_t *buffer,
194                 uint32_t offset,
195                 uint32_t count)
196 {
197         struct target           *target = bank->target;
198         uint32_t                target_buffer_size = 8192;
199         struct working_area     *helper;
200         struct working_area     *target_buffer;
201         uint32_t                address = bank->base + offset;
202         struct reg_param        reg_params[8];
203         int                     retval = ERROR_OK;
204         uint32_t                entry_point = 0, exit_point = 0;
205         uint32_t                res;
206         struct armv7m_algorithm armv7m_algo;
207
208         static const uint32_t aducm360_flash_write_code[] = {
209                         /* helper.code */
210                         0x88AF4D10, 0x0704F047, 0x682F80AF, 0x600E6806,
211                         0xF017882F, 0xF43F0F08, 0xF851AFFB, 0x42B77B04,
212                         0x800DF040, 0x0004F100, 0xF47F3A04, 0x686FAFEF,
213                         0x0704F027, 0xF04F80AF, 0xF0000400, 0xF04FB802,
214                         0xBE000480, 0x40002800, 0x00015000, 0x20000000,
215                         0x00013000
216         };
217
218         LOG_DEBUG("'aducm360_write_block_sync' requested, dst:0x%08" PRIx32 ", count:0x%08" PRIx32 "bytes.",
219                         address, count);
220
221         /*  ----- Check the destination area for a Long Word alignment -----  */
222         if (((count%4) != 0) || ((offset%4) != 0)) {
223                 LOG_ERROR("write block must be multiple of four bytes in offset & length");
224                 return ERROR_FAIL;
225         }
226
227         /*  ----- Allocate space in the target's RAM for the helper code -----  */
228         if (target_alloc_working_area(target, sizeof(aducm360_flash_write_code),
229                         &helper) != ERROR_OK) {
230                 LOG_WARNING("no working area available, can't do block memory writes");
231                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
232         }
233
234         /*  ----- Upload the helper code to the space in the target's RAM -----  */
235         uint8_t code[sizeof(aducm360_flash_write_code)];
236         target_buffer_set_u32_array(target, code, ARRAY_SIZE(aducm360_flash_write_code),
237                         aducm360_flash_write_code);
238         retval = target_write_buffer(target, helper->address, sizeof(code), code);
239         if (retval != ERROR_OK)
240                 return retval;
241         entry_point = helper->address;
242
243         /*  ----- Allocate space in the target's RAM for the user application's object code -----  */
244         while (target_alloc_working_area_try(target, target_buffer_size, &target_buffer) != ERROR_OK) {
245                 LOG_WARNING("couldn't allocate a buffer space of 0x%08" PRIx32 "bytes in the target's SRAM.",
246                                 target_buffer_size);
247                 target_buffer_size /= 2;
248                 if (target_buffer_size <= 256) {                /* No room available */
249                         LOG_WARNING("no large enough working area available, can't do block memory writes");
250                         target_free_working_area(target, helper);
251                         return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
252                 }
253         }
254
255         /* ----- Prepare the target for the helper ----- */
256         armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
257         armv7m_algo.core_mode = ARM_MODE_THREAD;
258
259         init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT); /*SRC      */
260         init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /*DST      */
261         init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /*COUNT    */
262         init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /*not used */
263         init_reg_param(&reg_params[4], "r4", 32, PARAM_IN);      /*RESULT   */
264
265         /*  ===== Execute the Main Programming Loop! ===== */
266         while (count > 0) {
267                 uint32_t thisrun_count = (count > target_buffer_size) ? target_buffer_size : count;
268
269                 /* ----- Upload the chunk ----- */
270                 retval = target_write_buffer(target, target_buffer->address, thisrun_count, buffer);
271                 if (retval != ERROR_OK)
272                         break;
273                 /* Set the arguments for the helper */
274                 buf_set_u32(reg_params[0].value, 0, 32, target_buffer->address);        /*SRC     */
275                 buf_set_u32(reg_params[1].value, 0, 32, address);                                       /*DST     */
276                 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);                         /*COUNT   */
277                 buf_set_u32(reg_params[3].value, 0, 32, 0);                                                     /*NOT USED*/
278
279                 retval = target_run_algorithm(target, 0, NULL, 5,
280                                 reg_params,     entry_point, exit_point, 10000, &armv7m_algo);
281                 if (retval != ERROR_OK) {
282                         LOG_ERROR("error executing aducm360 flash write algorithm");
283                         break;
284                 }
285
286                 res = buf_get_u32(reg_params[4].value, 0, 32);
287                 if (res) {
288                         LOG_ERROR("aducm360 fast sync algorithm reports an error (%02X)", res);
289                         retval = ERROR_FAIL;
290                         break;
291                 }
292
293                 buffer += thisrun_count;
294                 address += thisrun_count;
295                 count -= thisrun_count;
296         }
297
298         target_free_working_area(target, target_buffer);
299         target_free_working_area(target, helper);
300
301         destroy_reg_param(&reg_params[0]);
302         destroy_reg_param(&reg_params[1]);
303         destroy_reg_param(&reg_params[2]);
304         destroy_reg_param(&reg_params[3]);
305         destroy_reg_param(&reg_params[4]);
306
307         return retval;
308 }
309
310 /* ----------------------------------------------------------------------- */
311 static int aducm360_write_block_async(
312                 struct flash_bank *bank,
313                 const uint8_t *buffer,
314                 uint32_t offset,
315                 uint32_t count)
316 {
317         struct target           *target = bank->target;
318         uint32_t                target_buffer_size = 1024;
319         struct working_area     *helper;
320         struct working_area     *target_buffer;
321         uint32_t                address = bank->base + offset;
322         struct reg_param        reg_params[9];
323         int                     retval = ERROR_OK;
324         uint32_t                entry_point = 0, exit_point = 0;
325         uint32_t                res;
326         uint32_t                wcount;
327         struct armv7m_algorithm armv7m_algo;
328
329         static const uint32_t aducm360_flash_write_code[] = {
330                         /* helper.code */
331                         0x4050F8DF,     0xF04588A5,     0x80A50504,     0x8000F8D0,
332                         0x0F00F1B8, 0x8016F000, 0x45476847,     0xAFF6F43F,
333                         0x6B04F857, 0x6B04F842, 0xF0158825,     0xF43F0F08,
334                         0x428FAFFB, 0xF100BF28, 0x60470708,     0xB10B3B01,
335                         0xBFE4F7FF, 0xF02588A5, 0x80A50504,     0x0900F04F,
336                         0xBE00BF00, 0x40002800, 0x20000000,     0x20000100,
337                         0x00013000
338         };
339
340         LOG_DEBUG("'aducm360_write_block_async' requested, dst:0x%08" PRIx32 ", count:0x%08" PRIx32 "bytes.",
341                         address, count);
342
343         /*  ----- Check the destination area for a Long Word alignment -----  */
344         if (((count%4) != 0) || ((offset%4) != 0)) {
345                 LOG_ERROR("write block must be multiple of four bytes in offset & length");
346                 return ERROR_FAIL;
347         }
348         wcount = count/4;
349
350         /*  ----- Allocate space in the target's RAM for the helper code -----  */
351         if (target_alloc_working_area(target, sizeof(aducm360_flash_write_code),
352                         &helper) != ERROR_OK) {
353                 LOG_WARNING("no working area available, can't do block memory writes");
354                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
355         }
356
357         /*  ----- Upload the helper code to the space in the target's RAM -----  */
358         uint8_t code[sizeof(aducm360_flash_write_code)];
359         target_buffer_set_u32_array(target, code, ARRAY_SIZE(aducm360_flash_write_code),
360                         aducm360_flash_write_code);
361         retval = target_write_buffer(target, helper->address, sizeof(code), code);
362         if (retval != ERROR_OK)
363                 return retval;
364         entry_point = helper->address;
365
366         /*  ----- Allocate space in the target's RAM for the user application's object code ----- */
367         while (target_alloc_working_area_try(target, target_buffer_size, &target_buffer) != ERROR_OK) {
368                 LOG_WARNING("couldn't allocate a buffer space of 0x%08" PRIx32 "bytes in the target's SRAM.",
369                                 target_buffer_size);
370                 target_buffer_size /= 2;
371                 if (target_buffer_size <= 256) {                /* No room available */
372                         LOG_WARNING("no large enough working area available, can't do block memory writes");
373                         target_free_working_area(target, helper);
374                         return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
375                 }
376         }
377
378         /* ----- Prepare the target for the helper ----- */
379         armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
380         armv7m_algo.core_mode = ARM_MODE_THREAD;
381
382         init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT); /*SRCBEG     */
383         init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /*SRCEND     */
384         init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /*DST        */
385         init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /*COUNT (LWs)*/
386         init_reg_param(&reg_params[4], "r9", 32, PARAM_IN);  /*RESULT     */
387
388         buf_set_u32(reg_params[0].value, 0, 32, target_buffer->address);
389         buf_set_u32(reg_params[1].value, 0, 32, target_buffer->address + target_buffer->size);
390         buf_set_u32(reg_params[2].value, 0, 32, address);
391         buf_set_u32(reg_params[3].value, 0, 32, wcount);
392
393         retval = target_run_flash_async_algorithm(target, buffer, wcount, 4,
394                         0, NULL,
395                         5, reg_params,
396                         target_buffer->address, target_buffer->size,
397                         entry_point, exit_point,
398                         &armv7m_algo);
399         if (retval != ERROR_OK) {
400                 LOG_ERROR("error executing aducm360 flash write algorithm");
401         } else {
402                 res = buf_get_u32(reg_params[4].value, 0, 32);  /*RESULT*/
403                 if (res) {
404                         LOG_ERROR("aducm360 fast async algorithm reports an error (%02X)", res);
405                         retval = ERROR_FAIL;
406                 }
407         }
408
409         target_free_working_area(target, target_buffer);
410         target_free_working_area(target, helper);
411
412         destroy_reg_param(&reg_params[0]);
413         destroy_reg_param(&reg_params[1]);
414         destroy_reg_param(&reg_params[2]);
415         destroy_reg_param(&reg_params[3]);
416         destroy_reg_param(&reg_params[4]);
417
418         return retval;
419 }
420
421 /* ----------------------------------------------------------------------- */
422 /* If this fn returns ERROR_TARGET_RESOURCE_NOT_AVAILABLE, then the caller can fall
423  * back to another mechanism that does not require onboard RAM
424  *
425  * Caller should not check for other return values specifically
426  */
427 static int aducm360_write_block(struct flash_bank *bank,
428         const uint8_t *buffer,
429         uint32_t offset,
430         uint32_t count)
431 {
432         int     choice = 0;
433
434         switch (choice) {
435         case 0:
436                 return aducm360_write_block_sync(bank, buffer, offset, count);
437                 break;
438         case 1:
439                 return aducm360_write_block_async(bank, buffer, offset, count);
440                 break;
441         default:
442                 LOG_ERROR("aducm360_write_block was cancelled (no writing method was chosen)!");
443                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
444         }
445 }
446
447 /* ----------------------------------------------------------------------- */
448 #define FEESTA_WRDONE   0x00000008
449
450 static int aducm360_write_modified(struct flash_bank *bank,
451                 const uint8_t *buffer,
452                 uint32_t offset,
453                 uint32_t count)
454 {
455         uint32_t                value;
456         int                             res = ERROR_OK;
457         uint32_t        i, j, a, d;
458         struct target   *target = bank->target;
459
460         LOG_DEBUG("performing slow write (offset=0x%08" PRIx32 ", count=0x%08" PRIx32 ")...",
461                         offset, count);
462
463         /* Enable the writing to the flash */
464         aducm360_set_write_enable(target, 1);
465
466         /* Clear any old status */
467         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
468
469         for (i = 0; i < count; i += 4) {
470                 a = offset+i;
471                 for (j = 0; i < 4; i += 1)
472                         *((uint8_t *)(&d) + j) = buffer[i+j];
473                 target_write_u32(target, a, d);
474                 do {
475                         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
476                 } while (!(value & FEESTA_WRDONE));
477         }
478         aducm360_set_write_enable(target, 0);
479
480         return res;
481 }
482
483 /* ----------------------------------------------------------------------- */
484 static int aducm360_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
485 {
486         int retval;
487
488         /* try using a block write */
489         retval = aducm360_write_block(bank, buffer, offset, count);
490         if (retval != ERROR_OK) {
491                 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
492                         /* if block write failed (no sufficient working area),
493                          * use normal (slow) JTAG method */
494                         LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
495
496                         retval = aducm360_write_modified(bank, buffer, offset, count);
497                         if (retval != ERROR_OK) {
498                                 LOG_ERROR("slow write failed");
499                                 return ERROR_FLASH_OPERATION_FAILED;
500                         }
501                 }
502         }
503         return retval;
504 }
505
506 /* ----------------------------------------------------------------------- */
507 static int aducm360_probe(struct flash_bank *bank)
508 {
509         return ERROR_OK;
510 }
511
512 /* ----------------------------------------------------------------------- */
513 /* sets FEECON0 bit 2
514  * enable = 1 enables writes & erases, 0 disables them */
515 static int aducm360_set_write_enable(struct target *target, int enable)
516 {
517         /* don't bother to preserve int enable bit here */
518         uint32_t        value;
519
520         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEECON0, &value);
521         if (enable)
522                 value |= 0x00000004;
523         else
524                 value &= ~0x00000004;
525         target_write_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEECON0, value);
526
527         return ERROR_OK;
528 }
529
530 /* ----------------------------------------------------------------------- */
531 /* wait up to timeout_ms for controller to not be busy,
532  * then check whether the command passed or failed.
533  *
534  * this function sleeps 1ms between checks (after the first one),
535  * so in some cases may slow things down without a usleep after the first read */
536 static int aducm360_check_flash_completion(struct target *target, unsigned int timeout_ms)
537 {
538         uint32_t v = 1;
539
540         int64_t endtime = timeval_ms() + timeout_ms;
541         while (1) {
542                 target_read_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEESTA, &v);
543                 if ((v & 0x00000001) == 0)
544                         break;
545                 alive_sleep(1);
546                 if (timeval_ms() >= endtime)
547                         break;
548         }
549
550         if (!(v & 0x00000004))  /* b2 */
551                 return ERROR_FAIL;
552
553         return ERROR_OK;
554 }
555
556 /* ----------------------------------------------------------------------- */
557 const struct flash_driver aducm360_flash = {
558         .name = "aducm360",
559         .flash_bank_command = aducm360_flash_bank_command,
560         .erase = aducm360_erase,
561         .write = aducm360_write,
562         .read = default_flash_read,
563         .probe = aducm360_probe,
564         .auto_probe = aducm360_probe,
565         .erase_check = default_flash_blank_check,
566 };