|
|
|
@ -0,0 +1,997 @@
|
|
|
|
|
/***************************************************************************
|
|
|
|
|
* Copyright (C) 2016 by Maxim Integrated *
|
|
|
|
|
* Kevin Gillespie <kevin.gillespie@maximintegrated.com> *
|
|
|
|
|
* *
|
|
|
|
|
* 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 <http://www.gnu.org/licenses/>. *
|
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "imp.h"
|
|
|
|
|
#include <target/algorithm.h>
|
|
|
|
|
#include <target/armv7m.h>
|
|
|
|
|
|
|
|
|
|
/* 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 <base> <size> 0 0 <target> <FLC base> <sector size> <clkdiv> [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: <base> <size> 0 0 <target> <FLC base> <sector size> <clkdiv> [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 <bank>");
|
|
|
|
|
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 <bank> <addr> <size>");
|
|
|
|
|
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 <bank> <addr> <size>");
|
|
|
|
|
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 <bank> <addr> <size>");
|
|
|
|
|
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 <bank> <addr> <size>");
|
|
|
|
|
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 <bank> <addr> <size>");
|
|
|
|
|
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 <bank> <addr> <size>");
|
|
|
|
|
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 <bank>");
|
|
|
|
|
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:<sector number> a:<address> p:<protection bit>");
|
|
|
|
|
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,
|
|
|
|
|
};
|