PIC32: add flash algorithm support
[openocd/dsp568013.git] / src / flash / nor / pic32mx.c
blobc46264c5568698655057bad177661fe4d6b31d80
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2008 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
7 * *
8 * Copyright (C) 2008 by John McCarthy *
9 * jgmcc@magma.ca *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
25 ***************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
30 #include "imp.h"
31 #include "pic32mx.h"
32 #include <target/algorithm.h>
33 #include <target/mips32.h>
35 static const struct pic32mx_devs_s {
36 uint8_t devid;
37 char *name;
38 } pic32mx_devs[] = {
39 {0x38, "360F512L"},
40 {0x34, "360F256L"},
41 {0x2D, "340F128L"},
42 {0x2A, "320F128L"},
43 {0x16, "340F512H"},
44 {0x12, "340F256H"},
45 {0x0D, "340F128H"},
46 {0x0A, "320F128H"},
47 {0x06, "320F064H"},
48 {0x02, "320F032H"},
49 {0x07, "795F512L"},
50 {0x0E, "795F512H"},
51 {0x11, "675F512L"},
52 {0x0C, "675F512H"},
53 {0x0F, "575F512L"},
54 {0x09, "575F512H"},
55 {0x17, "575F256H"},
56 {0x78, "460F512L"},
57 {0x74, "460F256L"},
58 {0x6D, "440F128L"},
59 {0x56, "440F512H"},
60 {0x52, "440F256H"},
61 {0x4D, "440F128H"},
62 {0x42, "420F032H"},
63 {0x00, NULL}
66 /* flash bank pic32mx <base> <size> 0 0 <target#>
68 FLASH_BANK_COMMAND_HANDLER(pic32mx_flash_bank_command)
70 struct pic32mx_flash_bank *pic32mx_info;
72 if (CMD_ARGC < 6)
74 LOG_WARNING("incomplete flash_bank pic32mx configuration");
75 return ERROR_FLASH_BANK_INVALID;
78 pic32mx_info = malloc(sizeof(struct pic32mx_flash_bank));
79 bank->driver_priv = pic32mx_info;
81 pic32mx_info->write_algorithm = NULL;
82 pic32mx_info->probed = 0;
84 return ERROR_OK;
87 static uint32_t pic32mx_get_flash_status(struct flash_bank *bank)
89 struct target *target = bank->target;
90 uint32_t status;
92 target_read_u32(target, PIC32MX_NVMCON, &status);
94 return status;
97 static uint32_t pic32mx_wait_status_busy(struct flash_bank *bank, int timeout)
99 uint32_t status;
101 /* wait for busy to clear */
102 while (((status = pic32mx_get_flash_status(bank)) & NVMCON_NVMWR) && (timeout-- > 0))
104 LOG_DEBUG("status: 0x%" PRIx32, status);
105 alive_sleep(1);
107 if (timeout <= 0)
108 LOG_DEBUG("timeout: status: 0x%" PRIx32, status);
110 return status;
113 static int pic32mx_nvm_exec(struct flash_bank *bank, uint32_t op, uint32_t timeout)
115 struct target *target = bank->target;
116 uint32_t status;
118 target_write_u32(target, PIC32MX_NVMCON, NVMCON_NVMWREN | op);
120 /* unlock flash registers */
121 target_write_u32(target, PIC32MX_NVMKEY, NVMKEY1);
122 target_write_u32(target, PIC32MX_NVMKEY, NVMKEY2);
124 /* start operation */
125 target_write_u32(target, PIC32MX_NVMCONSET, NVMCON_NVMWR);
127 status = pic32mx_wait_status_busy(bank, timeout);
129 /* lock flash registers */
130 target_write_u32(target, PIC32MX_NVMCONCLR, NVMCON_NVMWREN);
132 return status;
135 static int pic32mx_protect_check(struct flash_bank *bank)
137 struct target *target = bank->target;
139 uint32_t devcfg0;
140 int s;
141 int num_pages;
143 if (target->state != TARGET_HALTED)
145 LOG_ERROR("Target not halted");
146 return ERROR_TARGET_NOT_HALTED;
149 target_read_u32(target, PIC32MX_DEVCFG0, &devcfg0);
151 if ((devcfg0 & (1 << 28)) == 0) /* code protect bit */
152 num_pages = 0xffff; /* All pages protected */
153 else if (Virt2Phys(bank->base) == PIC32MX_PHYS_BOOT_FLASH)
155 if (devcfg0 & (1 << 24))
156 num_pages = 0; /* All pages unprotected */
157 else
158 num_pages = 0xffff; /* All pages protected */
160 else /* pgm flash */
161 num_pages = (~devcfg0 >> 12) & 0xff;
163 for (s = 0; s < bank->num_sectors && s < num_pages; s++)
164 bank->sectors[s].is_protected = 1;
165 for (; s < bank->num_sectors; s++)
166 bank->sectors[s].is_protected = 0;
168 return ERROR_OK;
171 static int pic32mx_erase(struct flash_bank *bank, int first, int last)
173 struct target *target = bank->target;
174 int i;
175 uint32_t status;
177 if (bank->target->state != TARGET_HALTED)
179 LOG_ERROR("Target not halted");
180 return ERROR_TARGET_NOT_HALTED;
183 if ((first == 0) && (last == (bank->num_sectors - 1))
184 && (Virt2Phys(bank->base) == PIC32MX_PHYS_PGM_FLASH))
186 /* this will only erase the Program Flash (PFM), not the Boot Flash (BFM)
187 * we need to use the MTAP to perform a full erase */
188 LOG_DEBUG("Erasing entire program flash");
189 status = pic32mx_nvm_exec(bank, NVMCON_OP_PFM_ERASE, 50);
190 if (status & NVMCON_NVMERR)
191 return ERROR_FLASH_OPERATION_FAILED;
192 if (status & NVMCON_LVDERR)
193 return ERROR_FLASH_OPERATION_FAILED;
194 return ERROR_OK;
197 for (i = first; i <= last; i++)
199 target_write_u32(target, PIC32MX_NVMADDR, Virt2Phys(bank->base + bank->sectors[i].offset));
201 status = pic32mx_nvm_exec(bank, NVMCON_OP_PAGE_ERASE, 10);
203 if (status & NVMCON_NVMERR)
204 return ERROR_FLASH_OPERATION_FAILED;
205 if (status & NVMCON_LVDERR)
206 return ERROR_FLASH_OPERATION_FAILED;
207 bank->sectors[i].is_erased = 1;
210 return ERROR_OK;
213 static int pic32mx_protect(struct flash_bank *bank, int set, int first, int last)
215 struct pic32mx_flash_bank *pic32mx_info = NULL;
216 struct target *target = bank->target;
218 pic32mx_info = bank->driver_priv;
220 if (target->state != TARGET_HALTED)
222 LOG_ERROR("Target not halted");
223 return ERROR_TARGET_NOT_HALTED;
226 return ERROR_OK;
229 static const uint32_t pic32mx_flash_write_code[] = {
230 /* write: */
231 0x3C08AA99, /* lui $t0, 0xaa99 */
232 0x35086655, /* ori $t0, 0x6655 */
233 0x3C095566, /* lui $t1, 0x5566 */
234 0x352999AA, /* ori $t1, 0x99aa */
235 0x3C0ABF80, /* lui $t2, 0xbf80 */
236 0x354AF400, /* ori $t2, 0xf400 */
237 0x340B4003, /* ori $t3, $zero, 0x4003 */
238 0x340C8000, /* ori $t4, $zero, 0x8000 */
239 /* write_row: */
240 0x2CD30080, /* sltiu $s3, $a2, 128 */
241 0x16600008, /* bne $s3, $zero, write_word */
242 0x340D4000, /* ori $t5, $zero, 0x4000 */
243 0xAD450020, /* sw $a1, 32($t2) */
244 0xAD440040, /* sw $a0, 64($t2) */
245 0x04110016, /* bal progflash */
246 0x24840200, /* addiu $a0, $a0, 512 */
247 0x24A50200, /* addiu $a1, $a1, 512 */
248 0x1000FFF7, /* beq $zero, $zero, write_row */
249 0x24C6FF80, /* addiu $a2, $a2, -128 */
250 /* write_word: */
251 0x3C15A000, /* lui $s5, 0xa000 */
252 0x36B50000, /* ori $s5, $s5, 0x0 */
253 0x00952025, /* or $a0, $a0, $s5 */
254 0x10000008, /* beq $zero, $zero, next_word */
255 0x340B4001, /* ori $t3, $zero, 0x4001 */
256 /* prog_word: */
257 0x8C940000, /* lw $s4, 0($a0) */
258 0xAD540030, /* sw $s4, 48($t2) */
259 0xAD450020, /* sw $a1, 32($t2) */
260 0x04110009, /* bal progflash */
261 0x24840004, /* addiu $a0, $a0, 4 */
262 0x24A50004, /* addiu $a1, $a1, 4 */
263 0x24C6FFFF, /* addiu $a2, $a2, -1 */
264 /* next_word: */
265 0x14C0FFF8, /* bne $a2, $zero, prog_word */
266 0x00000000, /* nop */
267 /* done: */
268 0x10000002, /* beq $zero, $zero, exit */
269 0x24040000, /* addiu $a0, $zero, 0 */
270 /* error: */
271 0x26240000, /* addiu $a0, $s1, 0 */
272 /* exit: */
273 0x7000003F, /* sdbbp */
274 /* progflash: */
275 0xAD4B0000, /* sw $t3, 0($t2) */
276 0xAD480010, /* sw $t0, 16($t2) */
277 0xAD490010, /* sw $t1, 16($t2) */
278 0xAD4C0008, /* sw $t4, 8($t2) */
279 /* waitflash: */
280 0x8D500000, /* lw $s0, 0($t2) */
281 0x020C8024, /* and $s0, $s0, $t4 */
282 0x1600FFFD, /* bne $s0, $zero, waitflash */
283 0x00000000, /* nop */
284 0x00000000, /* nop */
285 0x00000000, /* nop */
286 0x00000000, /* nop */
287 0x00000000, /* nop */
288 0x8D510000, /* lw $s1, 0($t2) */
289 0x30113000, /* andi $s1, $zero, 0x3000 */
290 0x1620FFEF, /* bne $s1, $zero, error */
291 0xAD4D0004, /* sw $t5, 4($t2) */
292 0x03E00008, /* jr $ra */
293 0x00000000 /* nop */
296 static int pic32mx_write_block(struct flash_bank *bank, uint8_t *buffer,
297 uint32_t offset, uint32_t count)
299 struct target *target = bank->target;
300 uint32_t buffer_size = 16384;
301 struct working_area *source;
302 uint32_t address = bank->base + offset;
303 struct reg_param reg_params[3];
304 int retval = ERROR_OK;
306 struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
307 struct mips32_algorithm mips32_info;
309 /* flash write code */
310 if (target_alloc_working_area(target, sizeof(pic32mx_flash_write_code),
311 &pic32mx_info->write_algorithm) != ERROR_OK)
313 LOG_WARNING("no working area available, can't do block memory writes");
314 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
317 if ((retval = target_write_buffer(target,
318 pic32mx_info->write_algorithm->address,
319 sizeof(pic32mx_flash_write_code),
320 (uint8_t*)pic32mx_flash_write_code)) != ERROR_OK)
321 return retval;
323 /* memory buffer */
324 while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)
326 buffer_size /= 2;
327 if (buffer_size <= 256)
329 /* if we already allocated the writing code, but failed to get a
330 * buffer, free the algorithm */
331 if (pic32mx_info->write_algorithm)
332 target_free_working_area(target, pic32mx_info->write_algorithm);
334 LOG_WARNING("no large enough working area available, can't do block memory writes");
335 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
339 mips32_info.common_magic = MIPS32_COMMON_MAGIC;
340 mips32_info.isa_mode = MIPS32_ISA_MIPS32;
342 init_reg_param(&reg_params[0], "a0", 32, PARAM_IN_OUT);
343 init_reg_param(&reg_params[1], "a1", 32, PARAM_OUT);
344 init_reg_param(&reg_params[2], "a2", 32, PARAM_OUT);
346 while (count > 0)
348 uint32_t status;
349 uint32_t thisrun_count = (count > (buffer_size / 4)) ?
350 (buffer_size / 4) : count;
352 if ((retval = target_write_buffer(target, source->address,
353 thisrun_count * 4, buffer)) != ERROR_OK)
354 break;
356 buf_set_u32(reg_params[0].value, 0, 32, Virt2Phys(source->address));
357 buf_set_u32(reg_params[1].value, 0, 32, Virt2Phys(address));
358 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
360 if ((retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
361 pic32mx_info->write_algorithm->address,
362 pic32mx_info->write_algorithm->address + (sizeof(pic32mx_flash_write_code) - 76),
363 10000, &mips32_info)) != ERROR_OK)
365 LOG_ERROR("error executing pic32mx flash write algorithm");
366 retval = ERROR_FLASH_OPERATION_FAILED;
367 break;
370 status = buf_get_u32(reg_params[0].value, 0, 32);
372 if (status & NVMCON_NVMERR)
374 LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
375 retval = ERROR_FLASH_OPERATION_FAILED;
376 break;
379 if (status & NVMCON_LVDERR)
381 LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
382 retval = ERROR_FLASH_OPERATION_FAILED;
383 break;
386 buffer += thisrun_count * 4;
387 address += thisrun_count * 4;
388 count -= thisrun_count;
391 target_free_working_area(target, source);
392 target_free_working_area(target, pic32mx_info->write_algorithm);
394 destroy_reg_param(&reg_params[0]);
395 destroy_reg_param(&reg_params[1]);
396 destroy_reg_param(&reg_params[2]);
398 return retval;
401 static int pic32mx_write_word(struct flash_bank *bank, uint32_t address, uint32_t word)
403 struct target *target = bank->target;
405 target_write_u32(target, PIC32MX_NVMADDR, Virt2Phys(address));
406 target_write_u32(target, PIC32MX_NVMDATA, word);
408 return pic32mx_nvm_exec(bank, NVMCON_OP_WORD_PROG, 5);
411 static int pic32mx_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
413 uint32_t words_remaining = (count / 4);
414 uint32_t bytes_remaining = (count & 0x00000003);
415 uint32_t address = bank->base + offset;
416 uint32_t bytes_written = 0;
417 uint32_t status;
418 int retval;
420 if (bank->target->state != TARGET_HALTED)
422 LOG_ERROR("Target not halted");
423 return ERROR_TARGET_NOT_HALTED;
426 LOG_DEBUG("writing to flash at address 0x%08" PRIx32 " at offset 0x%8.8" PRIx32
427 " count: 0x%8.8" PRIx32 "", bank->base, offset, count);
429 if (offset & 0x3)
431 LOG_WARNING("offset 0x%" PRIx32 "breaks required 4-byte alignment", offset);
432 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
435 /* multiple words (4-byte) to be programmed? */
436 if (words_remaining > 0)
438 /* try using a block write */
439 if ((retval = pic32mx_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)
441 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
443 /* if block write failed (no sufficient working area),
444 * we use normal (slow) single dword accesses */
445 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
447 else if (retval == ERROR_FLASH_OPERATION_FAILED)
449 LOG_ERROR("flash writing failed with error code: 0x%x", retval);
450 return ERROR_FLASH_OPERATION_FAILED;
453 else
455 buffer += words_remaining * 4;
456 address += words_remaining * 4;
457 words_remaining = 0;
461 while (words_remaining > 0)
463 uint32_t value;
464 memcpy(&value, buffer + bytes_written, sizeof(uint32_t));
466 status = pic32mx_write_word(bank, address, value);
468 if (status & NVMCON_NVMERR)
470 LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
471 return ERROR_FLASH_OPERATION_FAILED;
474 if (status & NVMCON_LVDERR)
476 LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
477 return ERROR_FLASH_OPERATION_FAILED;
480 bytes_written += 4;
481 words_remaining--;
482 address += 4;
485 if (bytes_remaining)
487 uint32_t value = 0xffffffff;
488 memcpy(&value, buffer + bytes_written, bytes_remaining);
490 status = pic32mx_write_word(bank, address, value);
492 if (status & NVMCON_NVMERR)
494 LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
495 return ERROR_FLASH_OPERATION_FAILED;
498 if (status & NVMCON_LVDERR)
500 LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
501 return ERROR_FLASH_OPERATION_FAILED;
505 return ERROR_OK;
508 static int pic32mx_probe(struct flash_bank *bank)
510 struct target *target = bank->target;
511 struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
512 struct mips32_common *mips32 = target->arch_info;
513 struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
514 int i;
515 uint32_t num_pages = 0;
516 uint32_t device_id;
517 int page_size;
519 pic32mx_info->probed = 0;
521 device_id = ejtag_info->idcode;
522 LOG_INFO("device id = 0x%08" PRIx32 " (manuf 0x%03x dev 0x%02x, ver 0x%02x)",
523 device_id,
524 (unsigned)((device_id >> 1) & 0x7ff),
525 (unsigned)((device_id >> 12) & 0xff),
526 (unsigned)((device_id >> 28) & 0xf));
528 if (((device_id >> 1) & 0x7ff) != PIC32MX_MANUF_ID) {
529 LOG_WARNING("Cannot identify target as a PIC32MX family.");
530 return ERROR_FLASH_OPERATION_FAILED;
533 page_size = 4096;
535 if (Virt2Phys(bank->base) == PIC32MX_PHYS_BOOT_FLASH)
537 /* 0x1FC00000: Boot flash size */
538 #if 0
539 /* for some reason this register returns 8k for the boot bank size
540 * this does not match the docs, so for now set the boot bank at a
541 * fixed 12k */
542 if (target_read_u32(target, PIC32MX_BMXBOOTSZ, &num_pages) != ERROR_OK) {
543 LOG_WARNING("PIC32MX flash size failed, probe inaccurate - assuming 12k flash");
544 num_pages = (12 * 1024);
546 #else
547 /* fixed 12k boot bank - see comments above */
548 num_pages = (12 * 1024);
549 #endif
551 else
553 /* read the flash size from the device */
554 if (target_read_u32(target, PIC32MX_BMXPFMSZ, &num_pages) != ERROR_OK) {
555 LOG_WARNING("PIC32MX flash size failed, probe inaccurate - assuming 512k flash");
556 num_pages = (512 * 1024);
560 LOG_INFO("flash size = %dkbytes", num_pages / 1024);
562 /* calculate numbers of pages */
563 num_pages /= page_size;
564 bank->size = (num_pages * page_size);
565 bank->num_sectors = num_pages;
566 bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
568 for (i = 0; i < (int)num_pages; i++)
570 bank->sectors[i].offset = i * page_size;
571 bank->sectors[i].size = page_size;
572 bank->sectors[i].is_erased = -1;
573 bank->sectors[i].is_protected = 1;
576 pic32mx_info->probed = 1;
578 return ERROR_OK;
581 static int pic32mx_auto_probe(struct flash_bank *bank)
583 struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
584 if (pic32mx_info->probed)
585 return ERROR_OK;
586 return pic32mx_probe(bank);
589 static int pic32mx_info(struct flash_bank *bank, char *buf, int buf_size)
591 struct target *target = bank->target;
592 struct mips32_common *mips32 = target->arch_info;
593 struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
594 uint32_t device_id;
595 int printed = 0, i;
597 device_id = ejtag_info->idcode;
599 if (((device_id >> 1) & 0x7ff) != PIC32MX_MANUF_ID) {
600 snprintf(buf, buf_size,
601 "Cannot identify target as a PIC32MX family (manufacturer 0x%03d != 0x%03d)\n",
602 (unsigned)((device_id >> 1) & 0x7ff),
603 PIC32MX_MANUF_ID);
604 return ERROR_FLASH_OPERATION_FAILED;
607 for (i = 0; pic32mx_devs[i].name != NULL; i++)
609 if (pic32mx_devs[i].devid == ((device_id >> 12) & 0xff)) {
610 printed = snprintf(buf, buf_size, "PIC32MX%s", pic32mx_devs[i].name);
611 break;
615 if (pic32mx_devs[i].name == NULL) {
616 printed = snprintf(buf, buf_size, "Unknown");
619 buf += printed;
620 buf_size -= printed;
621 printed = snprintf(buf, buf_size, " Ver: 0x%02x",
622 (unsigned)((device_id >> 28) & 0xf));
624 return ERROR_OK;
627 COMMAND_HANDLER(pic32mx_handle_pgm_word_command)
629 uint32_t address, value;
630 int status, res;
632 if (CMD_ARGC != 3)
634 command_print(CMD_CTX, "pic32mx pgm_word <addr> <value> <bank>");
635 return ERROR_OK;
638 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
639 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
641 struct flash_bank *bank;
642 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 2, &bank);
643 if (ERROR_OK != retval)
644 return retval;
646 if (address < bank->base || address >= (bank->base + bank->size))
648 command_print(CMD_CTX, "flash address '%s' is out of bounds", CMD_ARGV[0]);
649 return ERROR_OK;
652 res = ERROR_OK;
653 status = pic32mx_write_word(bank, address, value);
654 if (status & NVMCON_NVMERR)
655 res = ERROR_FLASH_OPERATION_FAILED;
656 if (status & NVMCON_LVDERR)
657 res = ERROR_FLASH_OPERATION_FAILED;
659 if (res == ERROR_OK)
660 command_print(CMD_CTX, "pic32mx pgm word complete");
661 else
662 command_print(CMD_CTX, "pic32mx pgm word failed (status = 0x%x)", status);
664 return ERROR_OK;
667 static const struct command_registration pic32mx_exec_command_handlers[] = {
669 .name = "pgm_word",
670 .handler = pic32mx_handle_pgm_word_command,
671 .mode = COMMAND_EXEC,
672 .help = "program a word",
674 COMMAND_REGISTRATION_DONE
677 static const struct command_registration pic32mx_command_handlers[] = {
679 .name = "pic32mx",
680 .mode = COMMAND_ANY,
681 .help = "pic32mx flash command group",
682 .chain = pic32mx_exec_command_handlers,
684 COMMAND_REGISTRATION_DONE
687 struct flash_driver pic32mx_flash = {
688 .name = "pic32mx",
689 .commands = pic32mx_command_handlers,
690 .flash_bank_command = pic32mx_flash_bank_command,
691 .erase = pic32mx_erase,
692 .protect = pic32mx_protect,
693 .write = pic32mx_write,
694 .probe = pic32mx_probe,
695 .auto_probe = pic32mx_auto_probe,
696 .erase_check = default_flash_mem_blank_check,
697 .protect_check = pic32mx_protect_check,
698 .info = pic32mx_info,