flash/nor: Use proper data types in driver API
[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         uint32_t offset = 0;
89
90         /* sector size is 512 */
91         bank->num_sectors = bank->size / FLASH_SECTOR_SIZE;
92         bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
93         for (unsigned i = 0; i < bank->num_sectors; ++i) {
94                 bank->sectors[i].offset = offset;
95                 bank->sectors[i].size = FLASH_SECTOR_SIZE;
96                 offset += bank->sectors[i].size;
97                 bank->sectors[i].is_erased = -1;
98                 bank->sectors[i].is_protected = 0;
99         }
100
101         return ERROR_OK;
102 }
103
104 /* ----------------------------------------------------------------------- */
105 static int aducm360_mass_erase(struct target *target)
106 {
107         uint32_t                value;
108         int                             res = ERROR_OK;
109
110         /* Clear any old status */
111         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
112
113         /* Enable the writing to the flash*/
114         aducm360_set_write_enable(target, 1);
115
116         /* Unlock for writing */
117         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F456);
118         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F123);
119         /* Issue the 'MASSERASE' command */
120         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEECMD, 0x00000003);
121
122         /* Check the result */
123         res = aducm360_check_flash_completion(target, 3500);
124         if (res != ERROR_OK) {
125                 LOG_ERROR("mass erase failed.");
126                 aducm360_set_write_enable(target, 0);
127                 res = ERROR_FLASH_OPERATION_FAILED;
128         }
129
130         return res;
131 }
132
133 /* ----------------------------------------------------------------------- */
134 static int aducm360_page_erase(struct target *target, uint32_t padd)
135 {
136         uint32_t                value;
137         int                             res = ERROR_OK;
138
139         /* Clear any old status */
140         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
141
142         /* Enable the writing to the flash*/
143         aducm360_set_write_enable(target, 1);
144
145         /* Unlock for writing */
146         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F456);
147         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F123);
148         /* Write the sector address */
149         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEADR0L, padd & 0xFFFF);
150         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEADR0H, (padd>>16) & 0xFFFF);
151         /* Issue the 'ERASEPAGE' command */
152         target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEECMD, 0x00000001);
153
154         /* Check the result */
155         res = aducm360_check_flash_completion(target, 50);
156         if (res != ERROR_OK) {
157                 LOG_ERROR("page erase failed at 0x%08" PRIx32, padd);
158                 aducm360_set_write_enable(target, 0);
159                 res = ERROR_FLASH_OPERATION_FAILED;
160         }
161
162         return res;
163 }
164
165 /* ----------------------------------------------------------------------- */
166 static int aducm360_erase(struct flash_bank *bank, unsigned int first,
167                 unsigned 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         case 1:
438                 return aducm360_write_block_async(bank, buffer, offset, count);
439         default:
440                 LOG_ERROR("aducm360_write_block was cancelled (no writing method was chosen)!");
441                 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
442         }
443 }
444
445 /* ----------------------------------------------------------------------- */
446 #define FEESTA_WRDONE   0x00000008
447
448 static int aducm360_write_modified(struct flash_bank *bank,
449                 const uint8_t *buffer,
450                 uint32_t offset,
451                 uint32_t count)
452 {
453         uint32_t                value;
454         int                             res = ERROR_OK;
455         uint32_t        i, j, a, d;
456         struct target   *target = bank->target;
457
458         LOG_DEBUG("performing slow write (offset=0x%08" PRIx32 ", count=0x%08" PRIx32 ")...",
459                         offset, count);
460
461         /* Enable the writing to the flash */
462         aducm360_set_write_enable(target, 1);
463
464         /* Clear any old status */
465         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
466
467         for (i = 0; i < count; i += 4) {
468                 a = offset+i;
469                 for (j = 0; i < 4; i += 1)
470                         *((uint8_t *)(&d) + j) = buffer[i+j];
471                 target_write_u32(target, a, d);
472                 do {
473                         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
474                 } while (!(value & FEESTA_WRDONE));
475         }
476         aducm360_set_write_enable(target, 0);
477
478         return res;
479 }
480
481 /* ----------------------------------------------------------------------- */
482 static int aducm360_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
483 {
484         int retval;
485
486         /* try using a block write */
487         retval = aducm360_write_block(bank, buffer, offset, count);
488         if (retval != ERROR_OK) {
489                 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
490                         /* if block write failed (no sufficient working area),
491                          * use normal (slow) JTAG method */
492                         LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
493
494                         retval = aducm360_write_modified(bank, buffer, offset, count);
495                         if (retval != ERROR_OK) {
496                                 LOG_ERROR("slow write failed");
497                                 return ERROR_FLASH_OPERATION_FAILED;
498                         }
499                 }
500         }
501         return retval;
502 }
503
504 /* ----------------------------------------------------------------------- */
505 static int aducm360_probe(struct flash_bank *bank)
506 {
507         return ERROR_OK;
508 }
509
510 /* ----------------------------------------------------------------------- */
511 /* sets FEECON0 bit 2
512  * enable = 1 enables writes & erases, 0 disables them */
513 static int aducm360_set_write_enable(struct target *target, int enable)
514 {
515         /* don't bother to preserve int enable bit here */
516         uint32_t        value;
517
518         target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEECON0, &value);
519         if (enable)
520                 value |= 0x00000004;
521         else
522                 value &= ~0x00000004;
523         target_write_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEECON0, value);
524
525         return ERROR_OK;
526 }
527
528 /* ----------------------------------------------------------------------- */
529 /* wait up to timeout_ms for controller to not be busy,
530  * then check whether the command passed or failed.
531  *
532  * this function sleeps 1ms between checks (after the first one),
533  * so in some cases may slow things down without a usleep after the first read */
534 static int aducm360_check_flash_completion(struct target *target, unsigned int timeout_ms)
535 {
536         uint32_t v = 1;
537
538         int64_t endtime = timeval_ms() + timeout_ms;
539         while (1) {
540                 target_read_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEESTA, &v);
541                 if ((v & 0x00000001) == 0)
542                         break;
543                 alive_sleep(1);
544                 if (timeval_ms() >= endtime)
545                         break;
546         }
547
548         if (!(v & 0x00000004))  /* b2 */
549                 return ERROR_FAIL;
550
551         return ERROR_OK;
552 }
553
554 /* ----------------------------------------------------------------------- */
555 const struct flash_driver aducm360_flash = {
556         .name = "aducm360",
557         .flash_bank_command = aducm360_flash_bank_command,
558         .erase = aducm360_erase,
559         .write = aducm360_write,
560         .read = default_flash_read,
561         .probe = aducm360_probe,
562         .auto_probe = aducm360_probe,
563         .erase_check = default_flash_blank_check,
564 };