/*************************************************************************** * Copyright (C) 2016 by Maxim Integrated * * Kevin Gillespie * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "imp.h" #include #include /* Register Addresses */ #define FLSH_ADDR 0x000 #define FLSH_CLKDIV 0x004 #define FLSH_CN 0x008 #define PR1E_ADDR 0x00C #define PR2S_ADDR 0x010 #define PR2E_ADDR 0x014 #define PR3S_ADDR 0x018 #define PR3E_ADDR 0x01C #define FLSH_MD 0x020 #define FLSH_INT 0x024 #define FLSH_DATA0 0x030 #define FLSH_DATA1 0x034 #define FLSH_DATA2 0x038 #define FLSH_DATA3 0x03C #define FLSH_BL_CTRL 0x170 #define FLSH_PROT 0x300 #define ARM_PID_REG 0xE00FFFE0 #define MAX326XX_ID_REG 0x40000838 /* Register settings */ #define FLSH_INT_AF 0x00000002 #define FLSH_CN_UNLOCK_MASK 0xF0000000 #define FLSH_CN_UNLOCK_VALUE 0x20000000 #define FLSH_CN_PEND 0x01000000 #define FLSH_CN_ERASE_CODE_MASK 0x0000FF00 #define FLSH_CN_ERASE_CODE_PGE 0x00005500 #define FLSH_CN_ERASE_CODE_ME 0x0000AA00 #define FLSH_CN_PGE 0x00000004 #define FLSH_CN_ME 0x00000002 #define FLSH_CN_WR 0x00000001 #define FLASH_BL_CTRL_23 0x00020000 #define FLASH_BL_CTRL_IFREN 0x00000001 #define ARM_PID_DEFAULT_CM3 0xB4C3 #define ARM_PID_DEFAULT_CM4 0xB4C4 #define MAX326XX_ID 0x4D static int max32xxx_mass_erase(struct flash_bank *bank); struct max32xxx_flash_bank { int probed; int max326xx; unsigned int flash_size; unsigned int flc_base; unsigned int sector_size; unsigned int clkdiv_value; unsigned int int_state; unsigned int burst_size_bits; }; /* see contib/loaders/flash/max32xxx/max32xxx.s for src */ static const uint8_t write_code[] = { #include "../../contrib/loaders/flash/max32xxx/max32xxx.inc" }; /* Config Command: flash bank name driver base size chip_width bus_width target [driver_option] flash bank max32xxx 0 0 [burst_bits] */ FLASH_BANK_COMMAND_HANDLER(max32xxx_flash_bank_command) { struct max32xxx_flash_bank *info; if (CMD_ARGC < 9) { LOG_WARNING("incomplete flash bank max32xxx configuration: 0 0 [burst_bits]"); return ERROR_FLASH_BANK_INVALID; } info = calloc(sizeof(struct max32xxx_flash_bank), 1); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], info->flash_size); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->flc_base); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], info->sector_size); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[8], info->clkdiv_value); if (CMD_ARGC > 9) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[9], info->burst_size_bits); else info->burst_size_bits = 32; info->int_state = 0; bank->driver_priv = info; return ERROR_OK; } static int get_info(struct flash_bank *bank, char *buf, int buf_size) { int printed; struct max32xxx_flash_bank *info = bank->driver_priv; if (info->probed == 0) return ERROR_FLASH_BANK_NOT_PROBED; printed = snprintf(buf, buf_size, "\nMaxim Integrated max32xxx flash driver\n"); buf += printed; buf_size -= printed; return ERROR_OK; } /*************************************************************************** * flash operations ***************************************************************************/ static int max32xxx_flash_op_pre(struct flash_bank *bank) { struct target *target = bank->target; struct max32xxx_flash_bank *info = bank->driver_priv; uint32_t flsh_cn; uint32_t bootloader; /* Check if the flash controller is busy */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); if (flsh_cn & (FLSH_CN_PEND | FLSH_CN_ERASE_CODE_MASK | FLSH_CN_PGE | FLSH_CN_ME | FLSH_CN_WR)) return ERROR_FLASH_BUSY; /* Refresh flash controller timing */ target_write_u32(target, info->flc_base + FLSH_CLKDIV, info->clkdiv_value); /* Clear and disable flash programming interrupts */ target_read_u32(target, info->flc_base + FLSH_INT, &info->int_state); target_write_u32(target, info->flc_base + FLSH_INT, 0x00000000); /* Clear the lower bit in the bootloader configuration register in case flash page 0 has been replaced */ if (target_read_u32(target, info->flc_base + FLSH_BL_CTRL, &bootloader) != ERROR_OK) { LOG_ERROR("Read failure on FLSH_BL_CTRL"); return ERROR_FAIL; } if (bootloader & FLASH_BL_CTRL_23) { LOG_WARNING("FLSH_BL_CTRL indicates BL mode 2 or mode 3."); if (bootloader & FLASH_BL_CTRL_IFREN) { LOG_WARNING("Flash page 0 swapped out, attempting to swap back in for programming"); bootloader &= ~(FLASH_BL_CTRL_IFREN); if (target_write_u32(target, info->flc_base + FLSH_BL_CTRL, bootloader) != ERROR_OK) { LOG_ERROR("Write failure on FLSH_BL_CTRL"); return ERROR_FAIL; } if (target_read_u32(target, info->flc_base + FLSH_BL_CTRL, &bootloader) != ERROR_OK) { LOG_ERROR("Read failure on FLSH_BL_CTRL"); return ERROR_FAIL; } if (bootloader & FLASH_BL_CTRL_IFREN) { /* Bummer */ LOG_ERROR("Unable to swap flash page 0 back in. Writes to page 0 will fail."); } } } /* Unlock flash */ flsh_cn &= ~FLSH_CN_UNLOCK_MASK; flsh_cn |= FLSH_CN_UNLOCK_VALUE; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* Confirm flash is unlocked */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); if ((flsh_cn & FLSH_CN_UNLOCK_VALUE) != FLSH_CN_UNLOCK_VALUE) return ERROR_FAIL; return ERROR_OK; } static int max32xxx_flash_op_post(struct flash_bank *bank) { struct target *target = bank->target; struct max32xxx_flash_bank *info = bank->driver_priv; uint32_t flsh_cn; /* Restore flash programming interrupts */ target_write_u32(target, info->flc_base + FLSH_INT, info->int_state); /* Lock flash */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); flsh_cn &= ~FLSH_CN_UNLOCK_MASK; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); return ERROR_OK; } static int max32xxx_protect_check(struct flash_bank *bank) { struct max32xxx_flash_bank *info = bank->driver_priv; struct target *target = bank->target; int i; uint32_t temp_reg; if (info->probed == 0) return ERROR_FLASH_BANK_NOT_PROBED; if (!info->max326xx) { for (i = 0; i < bank->num_sectors; i++) bank->sectors[i].is_protected = -1; return ERROR_FLASH_OPER_UNSUPPORTED; } /* Check the protection */ for (i = 0; i < bank->num_sectors; i++) { if (i%32 == 0) target_read_u32(target, info->flc_base + FLSH_PROT + ((i/32)*4), &temp_reg); if (temp_reg & (0x1 << i%32)) bank->sectors[i].is_protected = 1; else bank->sectors[i].is_protected = 0; } return ERROR_OK; } static int max32xxx_erase(struct flash_bank *bank, int first, int last) { int banknr; uint32_t flsh_cn, flsh_int; struct max32xxx_flash_bank *info = bank->driver_priv; struct target *target = bank->target; int retval; int retry; if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } if (info->probed == 0) return ERROR_FLASH_BANK_NOT_PROBED; if ((first < 0) || (last < first) || (last >= bank->num_sectors)) return ERROR_FLASH_SECTOR_INVALID; if ((first == 0) && (last == (bank->num_sectors - 1))) return max32xxx_mass_erase(bank); /* Prepare to issue flash operation */ retval = max32xxx_flash_op_pre(bank); if (retval != ERROR_OK) return retval; int erased = 0; for (banknr = first; banknr <= last; banknr++) { /* Check the protection */ if (bank->sectors[banknr].is_protected == 1) { LOG_WARNING("Flash sector %d is protected", banknr); continue; } else erased = 1; /* Address is first word in page */ target_write_u32(target, info->flc_base + FLSH_ADDR, banknr * info->sector_size); /* Write page erase code */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); flsh_cn |= FLSH_CN_ERASE_CODE_PGE; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* Issue page erase command */ flsh_cn |= 0x4; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* Wait until erase complete */ retry = 1000; do { target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); if (retry <= 0) { LOG_ERROR("Timed out waiting for flash page erase @ 0x%08x", banknr * info->sector_size); return ERROR_FLASH_OPERATION_FAILED; } /* Check access violations */ target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); if (flsh_int & FLSH_INT_AF) { LOG_ERROR("Error erasing flash page %i", banknr); target_write_u32(target, info->flc_base + FLSH_INT, 0); max32xxx_flash_op_post(bank); return ERROR_FLASH_OPERATION_FAILED; } bank->sectors[banknr].is_erased = 1; } if (!erased) { LOG_ERROR("All pages protected %d to %d", first, last); max32xxx_flash_op_post(bank); return ERROR_FAIL; } if (max32xxx_flash_op_post(bank) != ERROR_OK) return ERROR_FAIL; return ERROR_OK; } static int max32xxx_protect(struct flash_bank *bank, int set, int first, int last) { struct max32xxx_flash_bank *info = bank->driver_priv; struct target *target = bank->target; int page; uint32_t temp_reg; if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } if (info->probed == 0) return ERROR_FLASH_BANK_NOT_PROBED; if (!info->max326xx) return ERROR_FLASH_OPER_UNSUPPORTED; if ((first < 0) || (last < first) || (last >= bank->num_sectors)) return ERROR_FLASH_SECTOR_INVALID; /* Setup the protection on the pages given */ for (page = first; page <= last; page++) { if (set) { /* Set the write/erase bit for this page */ target_read_u32(target, info->flc_base + FLSH_PROT + (page/32), &temp_reg); temp_reg |= (0x1 << page%32); target_write_u32(target, info->flc_base + FLSH_PROT + (page/32), temp_reg); bank->sectors[page].is_protected = 1; } else { /* Clear the write/erase bit for this page */ target_read_u32(target, info->flc_base + FLSH_PROT + (page/32), &temp_reg); temp_reg &= ~(0x1 << page%32); target_write_u32(target, info->flc_base + FLSH_PROT + (page/32), temp_reg); bank->sectors[page].is_protected = 0; } } return ERROR_OK; } static int max32xxx_write_block(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t wcount) { struct max32xxx_flash_bank *info = bank->driver_priv; struct target *target = bank->target; uint32_t buffer_size = 16384; struct working_area *source; struct working_area *write_algorithm; uint32_t address = bank->base + offset; struct reg_param reg_params[5]; struct armv7m_algorithm armv7m_info; int retval = ERROR_OK; /* power of two, and multiple of word size */ static const unsigned buf_min = 128; /* for small buffers it's faster not to download an algorithm */ if (wcount * 4 < buf_min) return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " wcount=%08" PRIx32 "", bank, buffer, offset, wcount); /* flash write code */ if (target_alloc_working_area(target, sizeof(write_code), &write_algorithm) != ERROR_OK) { LOG_DEBUG("no working area for block memory writes"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } /* plus a buffer big enough for this data */ if (wcount * 4 < buffer_size) buffer_size = wcount * 4; /* memory buffer */ while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { buffer_size /= 2; if (buffer_size <= buf_min) { target_free_working_area(target, write_algorithm); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } LOG_DEBUG("retry target_alloc_working_area(%s, size=%u)", target_name(target), (unsigned) buffer_size); } target_write_buffer(target, write_algorithm->address, sizeof(write_code), write_code); armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; armv7m_info.core_mode = ARM_MODE_THREAD; init_reg_param(®_params[0], "r0", 32, PARAM_OUT); init_reg_param(®_params[1], "r1", 32, PARAM_OUT); init_reg_param(®_params[2], "r2", 32, PARAM_OUT); init_reg_param(®_params[3], "r3", 32, PARAM_OUT); init_reg_param(®_params[4], "r4", 32, PARAM_OUT); buf_set_u32(reg_params[0].value, 0, 32, source->address); buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size); buf_set_u32(reg_params[2].value, 0, 32, address); buf_set_u32(reg_params[3].value, 0, 32, wcount); buf_set_u32(reg_params[4].value, 0, 32, info->flc_base); retval = target_run_flash_async_algorithm(target, buffer, wcount, 4, 0, NULL, 5, reg_params, source->address, source->size, write_algorithm->address, 0, &armv7m_info); if (retval == ERROR_FLASH_OPERATION_FAILED) LOG_ERROR("error %d executing max32xxx flash write algorithm", retval); target_free_working_area(target, write_algorithm); target_free_working_area(target, source); destroy_reg_param(®_params[0]); destroy_reg_param(®_params[1]); destroy_reg_param(®_params[2]); destroy_reg_param(®_params[3]); destroy_reg_param(®_params[4]); return retval; } static int max32xxx_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { struct max32xxx_flash_bank *info = bank->driver_priv; struct target *target = bank->target; uint32_t flsh_cn, flsh_int; uint32_t address = offset; uint32_t remaining = count; uint32_t words_remaining; int retval; int retry; if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } LOG_DEBUG("bank=%p buffer=%p offset=%08" PRIx32 " count=%08" PRIx32 "", bank, buffer, offset, count); if (info->probed == 0) return ERROR_FLASH_BANK_NOT_PROBED; if (offset & 0x3) { LOG_WARNING("offset size must be word aligned"); return ERROR_FLASH_DST_BREAKS_ALIGNMENT; } if (offset + count > bank->size) return ERROR_FLASH_DST_OUT_OF_BANK; /* Prepare to issue flash operation */ retval = max32xxx_flash_op_pre(bank); if (retval != ERROR_OK) return retval; if (remaining >= 4) { /* write in 32-bit units */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); flsh_cn &= 0xF7FFFFFF; flsh_cn |= 0x00000010; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* try using a block write */ words_remaining = remaining / 4; retval = max32xxx_write_block(bank, buffer, offset, words_remaining); if (retval != ERROR_OK) { if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) LOG_DEBUG("writing flash word-at-a-time"); else { max32xxx_flash_op_post(bank); return ERROR_FLASH_OPERATION_FAILED; } } else { /* all 32-bit words have been written */ buffer += words_remaining * 4; address += words_remaining * 4; remaining -= words_remaining * 4; } } if ((remaining >= 4) && ((address & 0x1F) != 0)) { /* write in 32-bit units until we are 128-bit aligned */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); flsh_cn &= 0xF7FFFFFF; flsh_cn |= 0x00000010; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); while ((remaining >= 4) && ((address & 0x1F) != 0)) { target_write_u32(target, info->flc_base + FLSH_ADDR, address); target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, buffer); flsh_cn |= 0x00000001; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* Wait until flash operation is complete */ retry = 10; do { target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); if (retry <= 0) { LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); return ERROR_FLASH_OPERATION_FAILED; } buffer += 4; address += 4; remaining -= 4; } } if ((info->burst_size_bits == 128) && (remaining >= 16)) { /* write in 128-bit bursts while we can */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); flsh_cn &= 0xFFFFFFEF; flsh_cn |= 0x08000000; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); target_write_u32(target, info->flc_base + FLSH_ADDR, address); while (remaining >= 16) { if ((address & 0xFFF) == 0) LOG_DEBUG("Writing @ 0x%08x", address); target_write_buffer(target, info->flc_base + FLSH_DATA0, 16, buffer); flsh_cn |= 0x00000001; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* Wait until flash operation is complete */ retry = 10; do { target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); if (retry <= 0) { LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); return ERROR_FLASH_OPERATION_FAILED; } buffer += 16; address += 16; remaining -= 16; } } if (remaining >= 4) { /* write in 32-bit units while we can */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); flsh_cn &= 0xF7FFFFFF; flsh_cn |= 0x00000010; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); while (remaining >= 4) { target_write_u32(target, info->flc_base + FLSH_ADDR, address); target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, buffer); flsh_cn |= 0x00000001; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* Wait until flash operation is complete */ retry = 10; do { target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); if (retry <= 0) { LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); return ERROR_FLASH_OPERATION_FAILED; } buffer += 4; address += 4; remaining -= 4; } } if (remaining > 0) { /* write remaining bytes in a 32-bit unit */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); flsh_cn &= 0xF7FFFFFF; flsh_cn |= 0x00000010; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff}; int i = 0; while (remaining > 0) { last_word[i++] = *buffer; buffer++; remaining--; } target_write_u32(target, info->flc_base + FLSH_ADDR, address); target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, last_word); flsh_cn |= 0x00000001; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* Wait until flash operation is complete */ retry = 10; do { target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); if (retry <= 0) { LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); return ERROR_FLASH_OPERATION_FAILED; } } /* Check access violations */ target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); if (flsh_int & FLSH_INT_AF) { LOG_ERROR("Flash Error writing 0x%x bytes at 0x%08x", count, offset); max32xxx_flash_op_post(bank); return ERROR_FLASH_OPERATION_FAILED; } if (max32xxx_flash_op_post(bank) != ERROR_OK) return ERROR_FAIL; return ERROR_OK; } static int max32xxx_probe(struct flash_bank *bank) { struct max32xxx_flash_bank *info = bank->driver_priv; struct target *target = bank->target; uint32_t arm_id[2]; uint16_t arm_pid; if (bank->sectors) { free(bank->sectors); bank->sectors = NULL; } /* provide this for the benefit of the NOR flash framework */ bank->size = info->flash_size; bank->num_sectors = info->flash_size / info->sector_size; bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector)); for (int i = 0; i < bank->num_sectors; i++) { bank->sectors[i].offset = i * info->sector_size; bank->sectors[i].size = info->sector_size; bank->sectors[i].is_erased = -1; bank->sectors[i].is_protected = -1; } /* Probe to determine if this part is in the max326xx family */ info->max326xx = 0; target_read_u32(target, ARM_PID_REG, &arm_id[0]); target_read_u32(target, ARM_PID_REG+4, &arm_id[1]); arm_pid = (arm_id[1] << 8) + arm_id[0]; LOG_DEBUG("arm_pid = 0x%x", arm_pid); if ((arm_pid == ARM_PID_DEFAULT_CM3) || arm_pid == ARM_PID_DEFAULT_CM4) { uint32_t max326xx_id; target_read_u32(target, MAX326XX_ID_REG, &max326xx_id); LOG_DEBUG("max326xx_id = 0x%x", max326xx_id); max326xx_id = ((max326xx_id & 0xFF000000) >> 24); if (max326xx_id == MAX326XX_ID) info->max326xx = 1; } LOG_DEBUG("info->max326xx = %d", info->max326xx); /* Initialize the protection bits for each flash page */ if (max32xxx_protect_check(bank) == ERROR_FLASH_OPER_UNSUPPORTED) LOG_WARNING("Flash protection not supported on this device"); info->probed = 1; return ERROR_OK; } static int max32xxx_mass_erase(struct flash_bank *bank) { struct target *target = NULL; struct max32xxx_flash_bank *info = NULL; uint32_t flsh_cn, flsh_int; int retval; int retry; info = bank->driver_priv; target = bank->target; if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } if (info->probed == 0) return ERROR_FLASH_BANK_NOT_PROBED; int not_protected = 0; for (int i = 0; i < bank->num_sectors; i++) { if (bank->sectors[i].is_protected == 1) LOG_WARNING("Flash sector %d is protected", i); else not_protected = 1; } if (!not_protected) { LOG_ERROR("All pages protected"); return ERROR_FAIL; } /* Prepare to issue flash operation */ retval = max32xxx_flash_op_pre(bank); if (retval != ERROR_OK) return retval; /* Write mass erase code */ target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); flsh_cn |= FLSH_CN_ERASE_CODE_ME; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* Issue mass erase command */ flsh_cn |= 0x2; target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); /* Wait until erase complete */ retry = 1000; do { target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); if (retry <= 0) { LOG_ERROR("Timed out waiting for flash mass erase"); return ERROR_FLASH_OPERATION_FAILED; } /* Check access violations */ target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); if (flsh_int & FLSH_INT_AF) { LOG_ERROR("Error mass erasing"); target_write_u32(target, info->flc_base + FLSH_INT, 0); return ERROR_FLASH_OPERATION_FAILED; } if (max32xxx_flash_op_post(bank) != ERROR_OK) return ERROR_FAIL; return ERROR_OK; } COMMAND_HANDLER(max32xxx_handle_mass_erase_command) { int i; struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); if (CMD_ARGC < 1) { command_print(CMD_CTX, "max32xxx mass_erase "); return ERROR_OK; } if (ERROR_OK != retval) return retval; if (max32xxx_mass_erase(bank) == ERROR_OK) { /* set all sectors as erased */ for (i = 0; i < bank->num_sectors; i++) bank->sectors[i].is_erased = 1; command_print(CMD_CTX, "max32xxx mass erase complete"); } else command_print(CMD_CTX, "max32xxx mass erase failed"); return ERROR_OK; } COMMAND_HANDLER(max32xxx_handle_protection_set_command) { struct flash_bank *bank; int retval; struct max32xxx_flash_bank *info; uint32_t addr, len; if (CMD_ARGC != 3) { command_print(CMD_CTX, "max32xxx protection_set "); return ERROR_OK; } retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); if (ERROR_OK != retval) return retval; info = bank->driver_priv; /* Convert the range to the page numbers */ if (1 != sscanf(CMD_ARGV[1], "0x%"SCNx32, &addr)) { LOG_WARNING("Error parsing address"); command_print(CMD_CTX, "max32xxx protection_set "); return ERROR_FAIL; } /* Mask off the top portion on the address */ addr = (addr & 0x0FFFFFFF); if (1 != sscanf(CMD_ARGV[2], "0x%"SCNx32, &len)) { LOG_WARNING("Error parsing length"); command_print(CMD_CTX, "max32xxx protection_set "); return ERROR_FAIL; } /* Check the address is in the range of the flash */ if ((addr+len) >= info->flash_size) return ERROR_FLASH_SECTOR_INVALID; if (len == 0) return ERROR_OK; /* Convert the address and length to the page boundaries */ addr = addr - (addr % info->sector_size); if (len % info->sector_size) len = len + info->sector_size - (len % info->sector_size); /* Convert the address and length to page numbers */ addr = (addr / info->sector_size); len = addr + (len / info->sector_size) - 1; if (max32xxx_protect(bank, 1, addr, len) == ERROR_OK) command_print(CMD_CTX, "max32xxx protection set complete"); else command_print(CMD_CTX, "max32xxx protection set failed"); return ERROR_OK; } COMMAND_HANDLER(max32xxx_handle_protection_clr_command) { struct flash_bank *bank; int retval; struct max32xxx_flash_bank *info; uint32_t addr, len; if (CMD_ARGC != 3) { command_print(CMD_CTX, "max32xxx protection_clr "); return ERROR_OK; } retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); if (ERROR_OK != retval) return retval; info = bank->driver_priv; /* Convert the range to the page numbers */ if (1 != sscanf(CMD_ARGV[1], "0x%"SCNx32, &addr)) { LOG_WARNING("Error parsing address"); command_print(CMD_CTX, "max32xxx protection_clr "); return ERROR_FAIL; } /* Mask off the top portion on the address */ addr = (addr & 0x0FFFFFFF); if (1 != sscanf(CMD_ARGV[2], "0x%"SCNx32, &len)) { LOG_WARNING("Error parsing length"); command_print(CMD_CTX, "max32xxx protection_clr "); return ERROR_FAIL; } /* Check the address is in the range of the flash */ if ((addr+len) >= info->flash_size) return ERROR_FLASH_SECTOR_INVALID; if (len == 0) return ERROR_OK; /* Convert the address and length to the page boundaries */ addr = addr - (addr % info->sector_size); if (len % info->sector_size) len = len + info->sector_size - (len % info->sector_size); /* Convert the address and length to page numbers */ addr = (addr / info->sector_size); len = addr + (len / info->sector_size) - 1; if (max32xxx_protect(bank, 0, addr, len) == ERROR_OK) command_print(CMD_CTX, "max32xxx protection clear complete"); else command_print(CMD_CTX, "max32xxx protection clear failed"); return ERROR_OK; } COMMAND_HANDLER(max32xxx_handle_protection_check_command) { struct flash_bank *bank; int retval; struct max32xxx_flash_bank *info; int i; if (CMD_ARGC < 1) { command_print(CMD_CTX, "max32xxx protection_check "); return ERROR_OK; } retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); if (ERROR_OK != retval) return retval; info = bank->driver_priv; /* Update the protection array */ retval = max32xxx_protect_check(bank); if (ERROR_OK != retval) { LOG_WARNING("Error updating the protection array"); return retval; } LOG_WARNING("s: a:
p:"); for (i = 0; i < bank->num_sectors; i += 4) { LOG_WARNING("s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d", (i+0), (i+0)*info->sector_size, bank->sectors[(i+0)].is_protected, (i+1), (i+1)*info->sector_size, bank->sectors[(i+1)].is_protected, (i+2), (i+2)*info->sector_size, bank->sectors[(i+2)].is_protected, (i+3), (i+3)*info->sector_size, bank->sectors[(i+3)].is_protected); } return ERROR_OK; } static const struct command_registration max32xxx_exec_command_handlers[] = { { .name = "mass_erase", .handler = max32xxx_handle_mass_erase_command, .mode = COMMAND_EXEC, .usage = "bank_id", .help = "mass erase flash", }, { .name = "protection_set", .handler = max32xxx_handle_protection_set_command, .mode = COMMAND_EXEC, .usage = "bank_id addr size", .help = "set flash protection for address range", }, { .name = "protection_clr", .handler = max32xxx_handle_protection_clr_command, .mode = COMMAND_EXEC, .usage = "bank_id addr size", .help = "clear flash protection for address range", }, { .name = "protection_check", .handler = max32xxx_handle_protection_check_command, .mode = COMMAND_EXEC, .usage = "bank_id", .help = "check flash protection", }, COMMAND_REGISTRATION_DONE }; static const struct command_registration max32xxx_command_handlers[] = { { .name = "max32xxx", .mode = COMMAND_EXEC, .help = "max32xxx flash command group", .chain = max32xxx_exec_command_handlers, }, COMMAND_REGISTRATION_DONE }; struct flash_driver max32xxx_flash = { .name = "max32xxx", .commands = max32xxx_command_handlers, .flash_bank_command = max32xxx_flash_bank_command, .erase = max32xxx_erase, .protect = max32xxx_protect, .write = max32xxx_write, .read = default_flash_read, .probe = max32xxx_probe, .auto_probe = max32xxx_probe, .erase_check = default_flash_blank_check, .protect_check = max32xxx_protect_check, .info = get_info, };