openocd/src/flash/nor/msp432.c
Edward Fewell 3e84da55a6 flash/nor: add support for TI MSP432 devices
Added msp432 flash driver to support the TI MSP432P4x and
MSP432E4x microcontrollers. Implemented the flash algo
helper as used in the TI debug and flash tools. This
implemention supports the MSP432E4, Falcon, and Falcon 2M
variants. The flash driver automatically detects the
connected variant and configures itself appropriately.
Added command to mass erase device for consistency with
TI tools and added command to unlock the protected BSL
region.

Tested using MSP432E401Y, MSP432P401R, and MSP432P4111
LaunchPads.
Tested with embedded XDS110 debug probe in CMSIS-DAP
mode and with external SEGGER J-Link probe.

Removed ti_msp432p4xx.cfg file made obsolete by this
patch.
Change-Id: I3b29d39ccc492524ef2c4a1733f7f9942c2684c0
Signed-off-by: Edward Fewell <efewell@ti.com>
Reviewed-on: http://openocd.zylin.com/4153
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
2018-07-18 21:09:23 +01:00

1104 lines
30 KiB
C

/***************************************************************************
* Copyright (C) 2018 by Texas Instruments, Inc. *
* *
* 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 "msp432.h"
#include <helper/binarybuffer.h>
#include <helper/time_support.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
#include <target/image.h>
/* MSP432P4 hardware registers */
#define P4_FLASH_MAIN_SIZE_REG 0xE0043020
#define P4_FLASH_INFO_SIZE_REG 0xE0043024
#define P4_DEVICE_ID_REG 0x0020100C
#define P4_HARDWARE_REV_REG 0x00201010
/* MSP432E4 hardware registers */
#define E4_DID0_REG 0x400FE000
#define E4_DID1_REG 0x400FE004
#define FLASH_TIMEOUT 8000
#define SUPPORT_MESSAGE \
"Your pre-production MSP432P401x silicon is not fully supported\n" \
"You can find more information at www.ti.com/product/MSP432P401R"
struct msp432_bank {
uint32_t device_id;
uint32_t hardware_rev;
int family_type;
int device_type;
uint32_t sector_length;
bool probed[2];
bool unlock_bsl;
struct working_area *working_area;
struct armv7m_algorithm armv7m_info;
};
static int msp432_auto_probe(struct flash_bank *bank);
static int msp432_device_type(uint32_t family_type, uint32_t device_id,
uint32_t hardware_rev)
{
int device_type = MSP432_NO_TYPE;
if (MSP432E4 == family_type) {
/* MSP432E4 device family */
if (device_id == 0x180C0002) {
if (hardware_rev == 0x102DC06E) {
/* The 01Y variant */
device_type = MSP432E401Y;
} else if (hardware_rev == 0x1032E076) {
/* The 11Y variant */
device_type = MSP432E411Y;
} else {
/* Reasonable guess that this is a new variant */
device_type = MSP432E4X_GUESS;
}
} else {
/* Wild guess that this is an MSP432E4 */
device_type = MSP432E4X_GUESS;
}
} else {
/* MSP432P4 device family */
/* Examine the device ID and hardware revision to get the device type */
switch (device_id) {
case 0xA000:
case 0xA001:
case 0xA002:
case 0xA003:
case 0xA004:
case 0xA005:
/* Device is definitely MSP432P401x, check hardware revision */
if (hardware_rev == 0x41 || hardware_rev == 0x42) {
/* Rev A or B of the silicon has been deprecated */
device_type = MSP432P401X_DEPR;
} else if (hardware_rev >= 0x43 && hardware_rev <= 0x49) {
/* Current and future revisions of the MSP432P401x device */
device_type = MSP432P401X;
} else {
/* Unknown or unanticipated hardware revision */
device_type = MSP432P401X_GUESS;
}
break;
case 0xA010:
case 0xA012:
case 0xA016:
case 0xA019:
case 0xA01F:
case 0xA020:
case 0xA022:
case 0xA026:
case 0xA029:
case 0xA02F:
/* Device is definitely MSP432P411x, check hardware revision */
if (hardware_rev >= 0x41 && hardware_rev <= 0x49) {
/* Current and future revisions of the MSP432P411x device */
device_type = MSP432P411X;
} else {
/* Unknown or unanticipated hardware revision */
device_type = MSP432P411X_GUESS;
}
break;
case 0xFFFF:
/* Device is very early silicon that has been deprecated */
device_type = MSP432P401X_DEPR;
break;
default:
if (device_id < 0xA010) {
/* Wild guess that this is an MSP432P401x */
device_type = MSP432P401X_GUESS;
} else {
/* Reasonable guess that this is a new variant */
device_type = MSP432P411X_GUESS;
}
break;
}
}
return device_type;
}
static const char *msp432_return_text(uint32_t return_code)
{
switch (return_code) {
case FLASH_BUSY:
return "FLASH_BUSY";
case FLASH_SUCCESS:
return "FLASH_SUCCESS";
case FLASH_ERROR:
return "FLASH_ERROR";
case FLASH_TIMEOUT_ERROR:
return "FLASH_TIMEOUT_ERROR";
case FLASH_VERIFY_ERROR:
return "FLASH_VERIFY_WRONG";
case FLASH_WRONG_COMMAND:
return "FLASH_WRONG_COMMAND";
case FLASH_POWER_ERROR:
return "FLASH_POWER_ERROR";
default:
return "UNDEFINED_RETURN_CODE";
}
}
static void msp432_init_params(struct msp432_algo_params *algo_params)
{
buf_set_u32(algo_params->flash_command, 0, 32, FLASH_NO_COMMAND);
buf_set_u32(algo_params->return_code, 0, 32, 0);
buf_set_u32(algo_params->_reserved0, 0, 32, 0);
buf_set_u32(algo_params->address, 0, 32, 0);
buf_set_u32(algo_params->length, 0, 32, 0);
buf_set_u32(algo_params->buffer1_status, 0, 32, BUFFER_INACTIVE);
buf_set_u32(algo_params->buffer2_status, 0, 32, BUFFER_INACTIVE);
buf_set_u32(algo_params->erase_param, 0, 32, FLASH_ERASE_MAIN);
buf_set_u32(algo_params->unlock_bsl, 0, 32, FLASH_LOCK_BSL);
}
static int msp432_exec_cmd(struct target *target, struct msp432_algo_params
*algo_params, uint32_t command)
{
int retval;
/* Make sure the given params do not include the command */
buf_set_u32(algo_params->flash_command, 0, 32, FLASH_NO_COMMAND);
buf_set_u32(algo_params->return_code, 0, 32, 0);
buf_set_u32(algo_params->buffer1_status, 0, 32, BUFFER_INACTIVE);
buf_set_u32(algo_params->buffer2_status, 0, 32, BUFFER_INACTIVE);
/* Write out parameters to target memory */
retval = target_write_buffer(target, ALGO_PARAMS_BASE_ADDR,
sizeof(struct msp432_algo_params), (uint8_t *)algo_params);
if (ERROR_OK != retval)
return retval;
/* Write out command to target memory */
retval = target_write_buffer(target, ALGO_FLASH_COMMAND_ADDR,
sizeof(command), (uint8_t *)&command);
return retval;
}
static int msp432_wait_return_code(struct target *target)
{
uint32_t return_code = 0;
long long start_ms;
long long elapsed_ms;
int retval = ERROR_OK;
start_ms = timeval_ms();
while ((0 == return_code) || (FLASH_BUSY == return_code)) {
retval = target_read_buffer(target, ALGO_RETURN_CODE_ADDR,
sizeof(return_code), (uint8_t *)&return_code);
if (ERROR_OK != retval)
return retval;
elapsed_ms = timeval_ms() - start_ms;
if (elapsed_ms > 500)
keep_alive();
if (elapsed_ms > FLASH_TIMEOUT)
break;
};
if (FLASH_SUCCESS != return_code) {
LOG_ERROR("msp432: Flash operation failed: %s",
msp432_return_text(return_code));
return ERROR_FAIL;
}
return ERROR_OK;
}
static int msp432_wait_inactive(struct target *target, uint32_t buffer)
{
uint32_t status_code = BUFFER_ACTIVE;
uint32_t status_addr;
long long start_ms;
long long elapsed_ms;
int retval;
switch (buffer) {
case 1: /* Buffer 1 */
status_addr = ALGO_BUFFER1_STATUS_ADDR;
break;
case 2: /* Buffer 2 */
status_addr = ALGO_BUFFER2_STATUS_ADDR;
break;
default:
return ERROR_FAIL;
}
start_ms = timeval_ms();
while (BUFFER_INACTIVE != status_code) {
retval = target_read_buffer(target, status_addr, sizeof(status_code),
(uint8_t *)&status_code);
if (ERROR_OK != retval)
return retval;
elapsed_ms = timeval_ms() - start_ms;
if (elapsed_ms > 500)
keep_alive();
if (elapsed_ms > FLASH_TIMEOUT)
break;
};
if (BUFFER_INACTIVE != status_code) {
LOG_ERROR(
"msp432: Flash operation failed: buffer not written to flash");
return ERROR_FAIL;
}
return ERROR_OK;
}
static int msp432_init(struct flash_bank *bank)
{
struct target *target = bank->target;
struct msp432_bank *msp432_bank = bank->driver_priv;
struct msp432_algo_params algo_params;
struct reg_param reg_params[1];
const uint8_t *loader_code;
uint32_t loader_size;
uint32_t algo_entry_addr;
int retval;
/* Make sure we've probed the flash to get the device and size */
retval = msp432_auto_probe(bank);
if (ERROR_OK != retval)
return retval;
/* Choose appropriate flash helper algorithm */
switch (msp432_bank->device_type) {
case MSP432P401X:
case MSP432P401X_DEPR:
case MSP432P401X_GUESS:
default:
loader_code = msp432p401x_algo;
loader_size = sizeof(msp432p401x_algo);
algo_entry_addr = P4_ALGO_ENTRY_ADDR;
break;
case MSP432P411X:
case MSP432P411X_GUESS:
loader_code = msp432p411x_algo;
loader_size = sizeof(msp432p411x_algo);
algo_entry_addr = P4_ALGO_ENTRY_ADDR;
break;
case MSP432E401Y:
case MSP432E411Y:
case MSP432E4X_GUESS:
loader_code = msp432e4x_algo;
loader_size = sizeof(msp432e4x_algo);
algo_entry_addr = E4_ALGO_ENTRY_ADDR;
break;
}
/* Issue warnings if this is a device we may not be able to flash */
if (MSP432P401X_GUESS == msp432_bank->device_type ||
MSP432P411X_GUESS == msp432_bank->device_type) {
/* Explicit device type check failed. Report this. */
LOG_WARNING(
"msp432: Unrecognized MSP432P4 Device ID and Hardware "
"Rev (%04X, %02X)", msp432_bank->device_id,
msp432_bank->hardware_rev);
} else if (MSP432P401X_DEPR == msp432_bank->device_type) {
LOG_WARNING(
"msp432: MSP432P401x pre-production device (deprecated "
"silicon)\n" SUPPORT_MESSAGE);
} else if (MSP432E4X_GUESS == msp432_bank->device_type) {
/* Explicit device type check failed. Report this. */
LOG_WARNING(
"msp432: Unrecognized MSP432E4 DID0 and DID1 values "
"(%08X, %08X)", msp432_bank->device_id,
msp432_bank->hardware_rev);
}
/* Check for working area to use for flash helper algorithm */
if (NULL != msp432_bank->working_area)
target_free_working_area(target, msp432_bank->working_area);
retval = target_alloc_working_area(target, ALGO_WORKING_SIZE,
&msp432_bank->working_area);
if (ERROR_OK != retval)
return retval;
/* Confirm the defined working address is the area we need to use */
if (ALGO_BASE_ADDR != msp432_bank->working_area->address)
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
/* Write flash helper algorithm into target memory */
retval = target_write_buffer(target, ALGO_BASE_ADDR, loader_size,
loader_code);
if (ERROR_OK != retval)
return retval;
/* Initialize the ARMv7 specific info to run the algorithm */
msp432_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
msp432_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
/* Initialize algorithm parameters to default values */
msp432_init_params(&algo_params);
/* Write out parameters to target memory */
retval = target_write_buffer(target, ALGO_PARAMS_BASE_ADDR,
sizeof(algo_params), (uint8_t *)&algo_params);
if (ERROR_OK != retval)
return retval;
/* Initialize stack pointer for flash helper algorithm */
init_reg_param(&reg_params[0], "sp", 32, PARAM_OUT);
buf_set_u32(reg_params[0].value, 0, 32, ALGO_STACK_POINTER_ADDR);
/* Begin executing the flash helper algorithm */
retval = target_start_algorithm(target, 0, 0, 1, reg_params,
algo_entry_addr, 0, &msp432_bank->armv7m_info);
destroy_reg_param(&reg_params[0]);
if (ERROR_OK != retval) {
LOG_ERROR("msp432: Failed to start flash helper algorithm");
return retval;
}
/*
* At this point, the algorithm is running on the target and
* ready to receive commands and data to flash the target
*/
/* Issue the init command to the flash helper algorithm */
retval = msp432_exec_cmd(target, &algo_params, FLASH_INIT);
if (ERROR_OK != retval)
return retval;
retval = msp432_wait_return_code(target);
return retval;
}
static int msp432_quit(struct flash_bank *bank)
{
struct target *target = bank->target;
struct msp432_bank *msp432_bank = bank->driver_priv;
struct msp432_algo_params algo_params;
int retval;
/* Initialize algorithm parameters to default values */
msp432_init_params(&algo_params);
/* Issue the exit command to the flash helper algorithm */
retval = msp432_exec_cmd(target, &algo_params, FLASH_EXIT);
if (ERROR_OK != retval)
return retval;
(void)msp432_wait_return_code(target);
/* Regardless of the return code, attempt to halt the target */
(void)target_halt(target);
/* Now confirm target halted and clean up from flash helper algorithm */
retval = target_wait_algorithm(target, 0, NULL, 0, NULL, 0, FLASH_TIMEOUT,
&msp432_bank->armv7m_info);
target_free_working_area(target, msp432_bank->working_area);
msp432_bank->working_area = NULL;
return retval;
}
static int msp432_mass_erase(struct flash_bank *bank, bool all)
{
struct target *target = bank->target;
struct msp432_bank *msp432_bank = bank->driver_priv;
struct msp432_algo_params algo_params;
int retval;
if (TARGET_HALTED != target->state) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
retval = msp432_init(bank);
if (ERROR_OK != retval)
return retval;
/* Initialize algorithm parameters to default values */
msp432_init_params(&algo_params);
if (all) {
buf_set_u32(algo_params.erase_param, 0, 32,
FLASH_ERASE_MAIN | FLASH_ERASE_INFO);
if (msp432_bank->unlock_bsl)
buf_set_u32(algo_params.unlock_bsl, 0, 32, FLASH_UNLOCK_BSL);
}
/* Issue the mass erase command to the flash helper algorithm */
retval = msp432_exec_cmd(target, &algo_params, FLASH_MASS_ERASE);
if (ERROR_OK != retval) {
(void)msp432_quit(bank);
return retval;
}
retval = msp432_wait_return_code(target);
if (ERROR_OK != retval) {
(void)msp432_quit(bank);
return retval;
}
retval = msp432_quit(bank);
if (ERROR_OK != retval)
return retval;
return retval;
}
COMMAND_HANDLER(msp432_mass_erase_command)
{
struct flash_bank *bank;
struct msp432_bank *msp432_bank;
bool all;
int retval;
if (0 == CMD_ARGC) {
all = false;
} else if (1 == CMD_ARGC) {
/* Check argument for how much to erase */
if (0 == strcmp(CMD_ARGV[0], "main"))
all = false;
else if (0 == strcmp(CMD_ARGV[0], "all"))
all = true;
else
return ERROR_COMMAND_SYNTAX_ERROR;
} else {
return ERROR_COMMAND_SYNTAX_ERROR;
}
retval = get_flash_bank_by_num(0, &bank);
if (ERROR_OK != retval)
return retval;
msp432_bank = bank->driver_priv;
if (MSP432E4 == msp432_bank->family_type) {
/* MSP432E4 does not have main vs info regions, ignore "all" */
all = false;
}
retval = msp432_mass_erase(bank, all);
if (ERROR_OK != retval)
return retval;
if (MSP432E4 == msp432_bank->family_type) {
/* MSP432E4 does not have main vs info regions */
LOG_INFO("msp432: Mass erase of flash is complete");
} else {
LOG_INFO("msp432: Mass erase of %s is complete",
all ? "main + info flash" : "main flash");
}
return ERROR_OK;
}
COMMAND_HANDLER(msp432_bsl_command)
{
struct flash_bank *bank;
struct msp432_bank *msp432_bank;
int retval;
if (1 < CMD_ARGC)
return ERROR_COMMAND_SYNTAX_ERROR;
retval = get_flash_bank_by_num(0, &bank);
if (ERROR_OK != retval)
return retval;
msp432_bank = bank->driver_priv;
if (MSP432E4 == msp432_bank->family_type) {
LOG_WARNING("msp432: MSP432E4 does not have a BSL region");
return ERROR_OK;
}
if (1 == CMD_ARGC) {
if (0 == strcmp(CMD_ARGV[0], "lock"))
msp432_bank->unlock_bsl = false;
else if (0 == strcmp(CMD_ARGV[0], "unlock"))
msp432_bank->unlock_bsl = true;
else
return ERROR_COMMAND_SYNTAX_ERROR;
}
LOG_INFO("msp432: BSL flash region is currently %slocked",
msp432_bank->unlock_bsl ? "un" : "");
return ERROR_OK;
}
FLASH_BANK_COMMAND_HANDLER(msp432_flash_bank_command)
{
struct msp432_bank *msp432_bank;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
msp432_bank = malloc(sizeof(struct msp432_bank));
if (NULL == msp432_bank)
return ERROR_FAIL;
/* Initialize private flash information */
msp432_bank->device_id = 0;
msp432_bank->hardware_rev = 0;
msp432_bank->family_type = MSP432_NO_FAMILY;
msp432_bank->device_type = MSP432_NO_TYPE;
msp432_bank->sector_length = 0x1000;
msp432_bank->probed[0] = false;
msp432_bank->probed[1] = false;
msp432_bank->unlock_bsl = false;
msp432_bank->working_area = NULL;
/* Finish initialization of bank 0 (main flash) */
bank->driver_priv = msp432_bank;
bank->next = NULL;
return ERROR_OK;
}
static int msp432_erase(struct flash_bank *bank, int first, int last)
{
struct target *target = bank->target;
struct msp432_bank *msp432_bank = bank->driver_priv;
struct msp432_algo_params algo_params;
int retval;
if (TARGET_HALTED != target->state) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* Do a mass erase if user requested all sectors of main flash */
if ((0 == bank->bank_number) && (first == 0) &&
(last == (bank->num_sectors - 1))) {
/* Request mass erase of main flash */
return msp432_mass_erase(bank, false);
}
retval = msp432_init(bank);
if (ERROR_OK != retval)
return retval;
/* Initialize algorithm parameters to default values */
msp432_init_params(&algo_params);
/* Adjust params if this is the info bank */
if (1 == bank->bank_number) {
buf_set_u32(algo_params.erase_param, 0, 32, FLASH_ERASE_INFO);
/* And flag if BSL is unlocked */
if (msp432_bank->unlock_bsl)
buf_set_u32(algo_params.unlock_bsl, 0, 32, FLASH_UNLOCK_BSL);
}
/* Erase requested sectors one by one */
for (int i = first; i <= last; i++) {
/* Skip TVL (read-only) sector of the info bank */
if (1 == bank->bank_number && 1 == i)
continue;
/* Skip BSL sectors of info bank if locked */
if (1 == bank->bank_number && (2 == i || 3 == i) &&
!msp432_bank->unlock_bsl)
continue;
/* Convert sector number to starting address of sector */
buf_set_u32(algo_params.address, 0, 32, bank->base +
(i * msp432_bank->sector_length));
/* Issue the sector erase command to the flash helper algorithm */
retval = msp432_exec_cmd(target, &algo_params, FLASH_SECTOR_ERASE);
if (ERROR_OK != retval) {
(void)msp432_quit(bank);
return retval;
}
retval = msp432_wait_return_code(target);
if (ERROR_OK != retval) {
(void)msp432_quit(bank);
return retval;
}
}
retval = msp432_quit(bank);
if (ERROR_OK != retval)
return retval;
return retval;
}
static int msp432_protect(struct flash_bank *bank, int set, int first,
int last)
{
return ERROR_OK;
}
static int msp432_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
struct msp432_bank *msp432_bank = bank->driver_priv;
struct msp432_algo_params algo_params;
uint32_t size;
uint32_t data_ready = BUFFER_DATA_READY;
long long start_ms;
long long elapsed_ms;
int retval;
if (TARGET_HALTED != target->state) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/*
* Block attempts to write to read-only sectors of flash
* The TVL region in sector 1 of the info flash is always read-only
* The BSL region in sectors 2 and 3 of the info flash may be unlocked
* The helper algorithm will hang on attempts to write to TVL
*/
if (1 == bank->bank_number) {
/* Set read-only start to TVL sector */
uint32_t start = 0x1000;
/* Set read-only end after BSL region if locked */
uint32_t end = (msp432_bank->unlock_bsl) ? 0x2000 : 0x4000;
/* Check if request includes anything in read-only sectors */
if ((offset + count - 1) < start || offset >= end) {
/* The request includes no bytes in read-only sectors */
/* Fall out and process the request normally */
} else {
/* Send a request for anything before read-only sectors */
if (offset < start) {
uint32_t start_count = MIN(start - offset, count);
retval = msp432_write(bank, buffer, offset, start_count);
if (ERROR_OK != retval)
return retval;
}
/* Send a request for anything after read-only sectors */
if ((offset + count - 1) >= end) {
uint32_t skip = end - offset;
count -= skip;
offset += skip;
buffer += skip;
return msp432_write(bank, buffer, offset, count);
} else {
/* Request is entirely in read-only sectors */
return ERROR_OK;
}
}
}
retval = msp432_init(bank);
if (ERROR_OK != retval)
return retval;
/* Initialize algorithm parameters to default values */
msp432_init_params(&algo_params);
/* Set up parameters for requested flash write operation */
buf_set_u32(algo_params.address, 0, 32, bank->base + offset);
buf_set_u32(algo_params.length, 0, 32, count);
/* Check if this is the info bank */
if (1 == bank->bank_number) {
/* And flag if BSL is unlocked */
if (msp432_bank->unlock_bsl)
buf_set_u32(algo_params.unlock_bsl, 0, 32, FLASH_UNLOCK_BSL);
}
/* Set up flash helper algorithm to continuous flash mode */
retval = msp432_exec_cmd(target, &algo_params, FLASH_CONTINUOUS);
if (ERROR_OK != retval) {
(void)msp432_quit(bank);
return retval;
}
/* Write requested data, one buffer at a time */
start_ms = timeval_ms();
while (count > 0) {
if (count > ALGO_BUFFER_SIZE)
size = ALGO_BUFFER_SIZE;
else
size = count;
/* Put next block of data to flash into buffer */
retval = target_write_buffer(target, ALGO_BUFFER1_ADDR, size, buffer);
if (ERROR_OK != retval) {
LOG_ERROR("Unable to write data to target memory");
(void)msp432_quit(bank);
return ERROR_FLASH_OPERATION_FAILED;
}
/* Signal the flash helper algorithm that data is ready to flash */
retval = target_write_buffer(target, ALGO_BUFFER1_STATUS_ADDR,
sizeof(data_ready), (uint8_t *)&data_ready);
if (ERROR_OK != retval) {
(void)msp432_quit(bank);
return ERROR_FLASH_OPERATION_FAILED;
}
retval = msp432_wait_inactive(target, 1);
if (ERROR_OK != retval) {
(void)msp432_quit(bank);
return retval;
}
count -= size;
buffer += size;
elapsed_ms = timeval_ms() - start_ms;
if (elapsed_ms > 500)
keep_alive();
}
/* Confirm that the flash helper algorithm is finished */
retval = msp432_wait_return_code(target);
if (ERROR_OK != retval) {
(void)msp432_quit(bank);
return retval;
}
retval = msp432_quit(bank);
if (ERROR_OK != retval)
return retval;
return retval;
}
static int msp432_probe(struct flash_bank *bank)
{
struct target *target = bank->target;
struct msp432_bank *msp432_bank = bank->driver_priv;
char *name;
uint32_t device_id;
uint32_t hardware_rev;
uint32_t base;
uint32_t sector_length;
uint32_t size;
int num_sectors;
int bank_id;
int retval;
bank_id = bank->bank_number;
/* Read the flash size register to determine this is a P4 or not */
/* MSP432P4s will return the size of flash. MSP432E4s will return zero */
retval = target_read_u32(target, P4_FLASH_MAIN_SIZE_REG, &size);
if (ERROR_OK != retval)
return retval;
if (0 == size) {
/* This is likely an MSP432E4 */
msp432_bank->family_type = MSP432E4;
retval = target_read_u32(target, E4_DID0_REG, &device_id);
if (ERROR_OK != retval)
return retval;
msp432_bank->device_id = device_id;
retval = target_read_u32(target, E4_DID1_REG, &hardware_rev);
if (ERROR_OK != retval)
return retval;
msp432_bank->hardware_rev = hardware_rev;
} else {
/* This is likely an MSP432P4 */
msp432_bank->family_type = MSP432P4;
retval = target_read_u32(target, P4_DEVICE_ID_REG, &device_id);
if (ERROR_OK != retval)
return retval;
msp432_bank->device_id = device_id & 0xFFFF;
retval = target_read_u32(target, P4_HARDWARE_REV_REG, &hardware_rev);
if (ERROR_OK != retval)
return retval;
msp432_bank->hardware_rev = hardware_rev & 0xFF;
}
msp432_bank->device_type = msp432_device_type(msp432_bank->family_type,
msp432_bank->device_id, msp432_bank->hardware_rev);
/* If not already allocated, create the info bank for MSP432P4 */
/* We could not determine it was needed until device was probed */
if (MSP432P4 == msp432_bank->family_type) {
/* If we've been given bank 1, then this was already done */
if (0 == bank_id) {
/* And only allocate it if it doesn't exist yet */
if (NULL == bank->next) {
struct flash_bank *info_bank;
info_bank = malloc(sizeof(struct flash_bank));
if (NULL == info_bank)
return ERROR_FAIL;
name = malloc(strlen(bank->name)+1);
if (NULL == name) {
free(info_bank);
return ERROR_FAIL;
}
strcpy(name, bank->name);
/* Initialize bank 1 (info region) */
info_bank->name = name;
info_bank->target = bank->target;
info_bank->driver = bank->driver;
info_bank->driver_priv = bank->driver_priv;
info_bank->bank_number = 1;
info_bank->base = 0x00200000;
info_bank->size = 0;
info_bank->chip_width = 0;
info_bank->bus_width = 0;
info_bank->erased_value = 0xff;
info_bank->default_padded_value = 0xff;
info_bank->write_start_alignment = 0;
info_bank->write_end_alignment = 0;
info_bank->minimal_write_gap = FLASH_WRITE_GAP_SECTOR;
info_bank->num_sectors = 0;
info_bank->sectors = NULL;
info_bank->num_prot_blocks = 0;
info_bank->prot_blocks = NULL;
info_bank->next = NULL;
/* Enable the new bank */
bank->next = info_bank;
}
}
}
if (MSP432P4 == msp432_bank->family_type) {
/* Set up MSP432P4 specific flash parameters */
if (0 == bank_id) {
retval = target_read_u32(target, P4_FLASH_MAIN_SIZE_REG, &size);
if (ERROR_OK != retval)
return retval;
base = P4_FLASH_MAIN_BASE;
sector_length = P4_SECTOR_LENGTH;
num_sectors = size / sector_length;
} else if (1 == bank_id) {
if (msp432_bank->device_type == MSP432P411X ||
msp432_bank->device_type == MSP432P411X_GUESS) {
/* MSP432P411x has an info size register, use that for size */
retval = target_read_u32(target, P4_FLASH_INFO_SIZE_REG, &size);
if (ERROR_OK != retval)
return retval;
} else {
/* All other MSP432P401x devices have fixed info region size */
size = 0x4000; /* 16 KB info region */
}
base = P4_FLASH_INFO_BASE;
sector_length = P4_SECTOR_LENGTH;
num_sectors = size / sector_length;
} else {
/* Invalid bank number somehow */
return ERROR_FAIL;
}
} else {
/* Set up MSP432E4 specific flash parameters */
base = E4_FLASH_BASE;
size = E4_FLASH_SIZE;
sector_length = E4_SECTOR_LENGTH;
num_sectors = size / sector_length;
}
if (NULL != bank->sectors) {
free(bank->sectors);
bank->sectors = NULL;
}
bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
if (NULL == bank->sectors)
return ERROR_FAIL;
bank->base = base;
bank->size = size;
bank->write_start_alignment = 0;
bank->write_end_alignment = 0;
bank->num_sectors = num_sectors;
msp432_bank->sector_length = sector_length;
for (int i = 0; i < num_sectors; i++) {
bank->sectors[i].offset = i * sector_length;
bank->sectors[i].size = sector_length;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 0;
}
/* We've successfully determined the stats on this flash bank */
msp432_bank->probed[bank_id] = true;
/* If we fall through to here, then all went well */
return ERROR_OK;
}
static int msp432_auto_probe(struct flash_bank *bank)
{
struct msp432_bank *msp432_bank = bank->driver_priv;
int retval = ERROR_OK;
if (bank->bank_number < 0 || bank->bank_number > 1) {
/* Invalid bank number somehow */
return ERROR_FAIL;
}
if (!msp432_bank->probed[bank->bank_number])
retval = msp432_probe(bank);
return retval;
}
static int msp432_protect_check(struct flash_bank *bank)
{
return ERROR_OK;
}
static int msp432_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct msp432_bank *msp432_bank = bank->driver_priv;
int printed = 0;
switch (msp432_bank->device_type) {
case MSP432P401X_DEPR:
if (0xFFFF == msp432_bank->device_id) {
/* Very early pre-production silicon currently deprecated */
printed = snprintf(buf, buf_size,
"MSP432P401x pre-production device (deprecated silicon)\n"
SUPPORT_MESSAGE);
} else {
/* Revision A or B silicon, also deprecated */
printed = snprintf(buf, buf_size,
"MSP432P401x Device Rev %c (deprecated silicon)\n"
SUPPORT_MESSAGE, (char)msp432_bank->hardware_rev);
}
break;
case MSP432P401X:
printed = snprintf(buf, buf_size,
"MSP432P401x Device Rev %c\n",
(char)msp432_bank->hardware_rev);
break;
case MSP432P411X:
printed = snprintf(buf, buf_size,
"MSP432P411x Device Rev %c\n",
(char)msp432_bank->hardware_rev);
break;
case MSP432E401Y:
printed = snprintf(buf, buf_size, "MSP432E401Y Device\n");
break;
case MSP432E411Y:
printed = snprintf(buf, buf_size, "MSP432E411Y Device\n");
break;
case MSP432E4X_GUESS:
printed = snprintf(buf, buf_size,
"Unrecognized MSP432E4 DID0 and DID1 IDs (%08X, %08X)",
msp432_bank->device_id, msp432_bank->hardware_rev);
break;
case MSP432P401X_GUESS:
case MSP432P411X_GUESS:
default:
printed = snprintf(buf, buf_size,
"Unrecognized MSP432P4 Device ID and Hardware Rev (%04X, %02X)",
msp432_bank->device_id, msp432_bank->hardware_rev);
break;
}
buf_size -= printed;
if (0 > buf_size)
return ERROR_BUF_TOO_SMALL;
return ERROR_OK;
}
static void msp432_flash_free_driver_priv(struct flash_bank *bank)
{
/* A single private struct is shared between main and info banks */
/* Only free it on the call for main bank (#0) */
if ((0 == bank->bank_number) && (NULL != bank->driver_priv))
free(bank->driver_priv);
/* Forget about the private struct on both main and info banks */
bank->driver_priv = NULL;
}
static const struct command_registration msp432_exec_command_handlers[] = {
{
.name = "mass_erase",
.handler = msp432_mass_erase_command,
.mode = COMMAND_EXEC,
.help = "Erase entire flash memory on device.",
.usage = "['main' | 'all']",
},
{
.name = "bsl",
.handler = msp432_bsl_command,
.mode = COMMAND_EXEC,
.help = "Allow BSL to be erased or written by flash commands.",
.usage = "['unlock' | 'lock']",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration msp432_command_handlers[] = {
{
.name = "msp432",
.mode = COMMAND_EXEC,
.help = "MSP432 flash command group",
.usage = "",
.chain = msp432_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver msp432_flash = {
.name = "msp432",
.commands = msp432_command_handlers,
.flash_bank_command = msp432_flash_bank_command,
.erase = msp432_erase,
.protect = msp432_protect,
.write = msp432_write,
.read = default_flash_read,
.probe = msp432_probe,
.auto_probe = msp432_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = msp432_protect_check,
.info = msp432_info,
.free_driver_priv = msp432_flash_free_driver_priv,
};