The function keep_alive() is optimized and return immediately if has nothing to do. There is no need to overly-complicate the code with extra counters or time computation plus the relative checks to reduce the number of calls to keep_alive(). Drop such extra code. Change-Id: I4574a3f154b5779f44105936c74af8fca1d2c49c Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com> Reviewed-on: https://review.openocd.org/c/openocd/+/9064 Reviewed-by: Tomas Vanek <vanekt@fbl.cz> Tested-by: jenkins Reviewed-by: Lucien Buchmann <lucien.buchmann@dufour.aero>
1132 lines
35 KiB
C
1132 lines
35 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/***************************************************************************
|
|
* Copyright (C) 2023-2025 Texas Instruments Incorporated - https://www.ti.com/
|
|
*
|
|
* NOR flash driver for MSPM0L and MSPM0G class of uC from Texas Instruments.
|
|
*
|
|
* See:
|
|
* https://www.ti.com/microcontrollers-mcus-processors/arm-based-microcontrollers/arm-cortex-m0-mcus/overview.html
|
|
***************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "imp.h"
|
|
#include <helper/bits.h>
|
|
#include <helper/time_support.h>
|
|
|
|
/* MSPM0 Region memory map */
|
|
#define MSPM0_FLASH_BASE_NONMAIN 0x41C00000
|
|
#define MSPM0_FLASH_END_NONMAIN 0x41C00400
|
|
#define MSPM0_FLASH_BASE_MAIN 0x0
|
|
#define MSPM0_FLASH_BASE_DATA 0x41D00000
|
|
|
|
/* MSPM0 FACTORYREGION registers */
|
|
#define MSPM0_FACTORYREGION 0x41C40000
|
|
#define MSPM0_TRACEID (MSPM0_FACTORYREGION + 0x000)
|
|
#define MSPM0_DID (MSPM0_FACTORYREGION + 0x004)
|
|
#define MSPM0_USERID (MSPM0_FACTORYREGION + 0x008)
|
|
#define MSPM0_SRAMFLASH (MSPM0_FACTORYREGION + 0x018)
|
|
|
|
/* MSPM0 FCTL registers */
|
|
#define FLASH_CONTROL_BASE 0x400CD000
|
|
#define FCTL_REG_DESC (FLASH_CONTROL_BASE + 0x10FC)
|
|
#define FCTL_REG_CMDEXEC (FLASH_CONTROL_BASE + 0x1100)
|
|
#define FCTL_REG_CMDTYPE (FLASH_CONTROL_BASE + 0x1104)
|
|
#define FCTL_REG_CMDADDR (FLASH_CONTROL_BASE + 0x1120)
|
|
#define FCTL_REG_CMDBYTEN (FLASH_CONTROL_BASE + 0x1124)
|
|
#define FCTL_REG_CMDDATA0 (FLASH_CONTROL_BASE + 0x1130)
|
|
#define FCTL_REG_CMDWEPROTA (FLASH_CONTROL_BASE + 0x11D0)
|
|
#define FCTL_REG_CMDWEPROTB (FLASH_CONTROL_BASE + 0x11D4)
|
|
#define FCTL_REG_CMDWEPROTNM (FLASH_CONTROL_BASE + 0x1210)
|
|
#define FCTL_REG_STATCMD (FLASH_CONTROL_BASE + 0x13D0)
|
|
|
|
/* FCTL_STATCMD[CMDDONE] Bits */
|
|
#define FCTL_STATCMD_CMDDONE_MASK 0x00000001
|
|
#define FCTL_STATCMD_CMDDONE_STATDONE 0x00000001
|
|
|
|
/* FCTL_STATCMD[CMDPASS] Bits */
|
|
#define FCTL_STATCMD_CMDPASS_MASK 0x00000002
|
|
#define FCTL_STATCMD_CMDPASS_STATPASS 0x00000002
|
|
|
|
/*
|
|
* FCTL_CMDEXEC Bits
|
|
* FCTL_CMDEXEC[VAL] Bits
|
|
*/
|
|
#define FCTL_CMDEXEC_VAL_EXECUTE 0x00000001
|
|
|
|
/* FCTL_CMDTYPE[COMMAND] Bits */
|
|
#define FCTL_CMDTYPE_COMMAND_PROGRAM 0x00000001
|
|
#define FCTL_CMDTYPE_COMMAND_ERASE 0x00000002
|
|
|
|
/* FCTL_CMDTYPE[SIZE] Bits */
|
|
#define FCTL_CMDTYPE_SIZE_ONEWORD 0x00000000
|
|
#define FCTL_CMDTYPE_SIZE_SECTOR 0x00000040
|
|
|
|
/* FCTL_FEATURE_VER_B minimum */
|
|
#define FCTL_FEATURE_VER_B 0xA
|
|
|
|
#define MSPM0_MAX_PROTREGS 3
|
|
|
|
#define MSPM0_FLASH_TIMEOUT_MS 8000
|
|
#define ERR_STRING_MAX 255
|
|
|
|
/* SYSCTL BASE */
|
|
#define SYSCTL_BASE 0x400AF000
|
|
#define SYSCTL_SECCFG_SECSTATUS (SYSCTL_BASE + 0x00003048)
|
|
|
|
/* TI manufacturer ID */
|
|
#define TI_MANUFACTURER_ID 0x17
|
|
|
|
/* Defines for probe status */
|
|
#define MSPM0_NO_ID_FOUND 0
|
|
#define MSPM0_DEV_ID_FOUND 1
|
|
#define MSPM0_DEV_PART_ID_FOUND 2
|
|
|
|
struct mspm0_flash_bank {
|
|
/* chip id register */
|
|
uint32_t did;
|
|
/* Device Unique ID register */
|
|
uint32_t traceid;
|
|
unsigned char version;
|
|
|
|
const char *name;
|
|
|
|
/* Decoded flash information */
|
|
unsigned int data_flash_size_kb;
|
|
unsigned int main_flash_size_kb;
|
|
unsigned int main_flash_num_banks;
|
|
unsigned int sector_size;
|
|
/* Decoded SRAM information */
|
|
unsigned int sram_size_kb;
|
|
|
|
/* Flash word size: 64 bit = 8, 128bit = 16 bytes */
|
|
unsigned char flash_word_size_bytes;
|
|
|
|
/* Protection register stuff */
|
|
unsigned int protect_reg_base;
|
|
unsigned int protect_reg_count;
|
|
|
|
/* Flashctl version: A - CMDWEPROTA/B, B- CMDWEPROTB */
|
|
unsigned char flash_version;
|
|
};
|
|
|
|
struct mspm0_part_info {
|
|
const char *part_name;
|
|
unsigned short part;
|
|
unsigned char variant;
|
|
};
|
|
|
|
struct mspm0_family_info {
|
|
const char *family_name;
|
|
unsigned short part_num;
|
|
unsigned char part_count;
|
|
const struct mspm0_part_info *part_info;
|
|
};
|
|
|
|
/* https://www.ti.com/lit/ds/symlink/mspm0l1346.pdf Table 8-13 and so on */
|
|
static const struct mspm0_part_info mspm0l_parts[] = {
|
|
{ "MSPM0L1105TDGS20R", 0x51DB, 0x16 },
|
|
{ "MSPM0L1105TDGS28R", 0x51DB, 0x83 },
|
|
{ "MSPM0L1105TDYYR", 0x51DB, 0x54 },
|
|
{ "MSPM0L1105TRGER", 0x51DB, 0x86 },
|
|
{ "MSPM0L1105TRHBR", 0x51DB, 0x68 },
|
|
{ "MSPM0L1106TDGS20R", 0x5552, 0x4B },
|
|
{ "MSPM0L1106TDGS28R", 0x5552, 0x98 },
|
|
{ "MSPM0L1106TDYYR", 0x5552, 0x9D },
|
|
{ "MSPM0L1106TRGER", 0x5552, 0x90 },
|
|
{ "MSPM0L1106TRHBR", 0x5552, 0x53 },
|
|
{ "MSPM0L1303SRGER", 0xef0, 0x17 },
|
|
{ "MSPM0L1303TRGER", 0xef0, 0xe2 },
|
|
{ "MSPM0L1304QDGS20R", 0xd717, 0x91 },
|
|
{ "MSPM0L1304QDGS28R", 0xd717, 0xb6 },
|
|
{ "MSPM0L1304QDYYR", 0xd717, 0xa0 },
|
|
{ "MSPM0L1304QRHBR", 0xd717, 0xa9 },
|
|
{ "MSPM0L1304SDGS20R", 0xd717, 0xfa },
|
|
{ "MSPM0L1304SDGS28R", 0xd717, 0x73 },
|
|
{ "MSPM0L1304SDYYR", 0xd717, 0xb7 },
|
|
{ "MSPM0L1304SRGER", 0xd717, 0x26 },
|
|
{ "MSPM0L1304SRHBR", 0xd717, 0xe4 },
|
|
{ "MSPM0L1304TDGS20R", 0xd717, 0x33 },
|
|
{ "MSPM0L1304TDGS28R", 0xd717, 0xa8 },
|
|
{ "MSPM0L1304TDYYR", 0xd717, 0xf9 },
|
|
{ "MSPM0L1304TRGER", 0xd717, 0xb7 },
|
|
{ "MSPM0L1304TRHBR", 0xd717, 0x5a },
|
|
{ "MSPM0L1305QDGS20R", 0x4d03, 0xb7 },
|
|
{ "MSPM0L1305QDGS28R", 0x4d03, 0x74 },
|
|
{ "MSPM0L1305QDYYR", 0x4d03, 0xec },
|
|
{ "MSPM0L1305QRHBR", 0x4d03, 0x78 },
|
|
{ "MSPM0L1305SDGS20R", 0x4d03, 0xc7 },
|
|
{ "MSPM0L1305SDGS28R", 0x4d03, 0x64 },
|
|
{ "MSPM0L1305SDYYR", 0x4d03, 0x91 },
|
|
{ "MSPM0L1305SRGER", 0x4d03, 0x73 },
|
|
{ "MSPM0L1305SRHBR", 0x4d03, 0x2d },
|
|
{ "MSPM0L1305TDGS20R", 0x4d03, 0xa0 },
|
|
{ "MSPM0L1305TDGS28R", 0x4d03, 0xfb },
|
|
{ "MSPM0L1305TDYYR", 0x4d03, 0xde },
|
|
{ "MSPM0L1305TRGER", 0x4d03, 0xea },
|
|
{ "MSPM0L1305TRHBR", 0x4d03, 0x85 },
|
|
{ "MSPM0L1306QDGS20R", 0xbb70, 0x59 },
|
|
{ "MSPM0L1306QDGS28R", 0xbb70, 0xf7 },
|
|
{ "MSPM0L1306QDYYR", 0xbb70, 0x9f },
|
|
{ "MSPM0L1306QRHBR", 0xbb70, 0xc2 },
|
|
{ "MSPM0L1306SDGS20R", 0xbb70, 0xf4 },
|
|
{ "MSPM0L1306SDGS28R", 0xbb70, 0x5 },
|
|
{ "MSPM0L1306SDYYR", 0xbb70, 0xe },
|
|
{ "MSPM0L1306SRGER", 0xbb70, 0x7f },
|
|
{ "MSPM0L1306SRHBR", 0xbb70, 0x3c },
|
|
{ "MSPM0L1306TDGS20R", 0xbb70, 0xa },
|
|
{ "MSPM0L1306TDGS28R", 0xbb70, 0x63 },
|
|
{ "MSPM0L1306TDYYR", 0xbb70, 0x35 },
|
|
{ "MSPM0L1306TRGER", 0xbb70, 0xaa },
|
|
{ "MSPM0L1306TRHBR", 0xbb70, 0x52 },
|
|
{ "MSPM0L1343TDGS20R", 0xb231, 0x2e },
|
|
{ "MSPM0L1344TDGS20R", 0x40b0, 0xd0 },
|
|
{ "MSPM0L1345TDGS28R", 0x98b4, 0x74 },
|
|
{ "MSPM0L1346TDGS28R", 0xf2b5, 0xef },
|
|
};
|
|
|
|
/* https://www.ti.com/lit/ds/symlink/mspm0g3506.pdf Table 8-20 */
|
|
static const struct mspm0_part_info mspm0g_parts[] = {
|
|
{ "MSPM0G1105TPTR", 0x8934, 0xD },
|
|
{ "MSPM0G1105TRGZR", 0x8934, 0xFE },
|
|
{ "MSPM0G1106TPMR", 0x477B, 0xD4 },
|
|
{ "MSPM0G1106TPTR", 0x477B, 0x71 },
|
|
{ "MSPM0G1106TRGZR", 0x477B, 0xBB },
|
|
{ "MSPM0G1106TRHBR", 0x477B, 0x0 },
|
|
{ "MSPM0G1107TDGS28R", 0x807B, 0x82 },
|
|
{ "MSPM0G1107TPMR", 0x807B, 0xB3 },
|
|
{ "MSPM0G1107TPTR", 0x807B, 0x32 },
|
|
{ "MSPM0G1107TRGER", 0x807B, 0x79 },
|
|
{ "MSPM0G1107TRGZR", 0x807B, 0x20 },
|
|
{ "MSPM0G1107TRHBR", 0x807B, 0xBC },
|
|
{ "MSPM0G1505SDGS28R", 0x13C4, 0x73 },
|
|
{ "MSPM0G1505SPMR", 0x13C4, 0x53 },
|
|
{ "MSPM0G1505SPTR", 0x13C4, 0x3E },
|
|
{ "MSPM0G1505SRGER", 0x13C4, 0x47 },
|
|
{ "MSPM0G1505SRGZR", 0x13C4, 0x34 },
|
|
{ "MSPM0G1505SRHBR", 0x13C4, 0x30 },
|
|
{ "MSPM0G1506SDGS28R", 0x5AE0, 0x3A },
|
|
{ "MSPM0G1506SPMR", 0x5AE0, 0xF6 },
|
|
{ "MSPM0G1506SRGER", 0x5AE0, 0x67 },
|
|
{ "MSPM0G1506SRGZR", 0x5AE0, 0x75 },
|
|
{ "MSPM0G1506SRHBR", 0x5AE0, 0x57 },
|
|
{ "MSPM0G1507SDGS28R", 0x2655, 0x6D },
|
|
{ "MSPM0G1507SPMR", 0x2655, 0x97 },
|
|
{ "MSPM0G1507SRGER", 0x2655, 0x83 },
|
|
{ "MSPM0G1507SRGZR", 0x2655, 0xD3 },
|
|
{ "MSPM0G1507SRHBR", 0x2655, 0x4D },
|
|
{ "MSPM0G3105SDGS20R", 0x4749, 0x21 },
|
|
{ "MSPM0G3105SDGS28R", 0x4749, 0xDD },
|
|
{ "MSPM0G3105SRHBR", 0x4749, 0xBE },
|
|
{ "MSPM0G3106SDGS20R", 0x54C7, 0xD2 },
|
|
{ "MSPM0G3106SDGS28R", 0x54C7, 0xB9 },
|
|
{ "MSPM0G3106SRHBR", 0x54C7, 0x67 },
|
|
{ "MSPM0G3107SDGS20R", 0xAB39, 0x5C },
|
|
{ "MSPM0G3107SDGS28R", 0xAB39, 0xCC },
|
|
{ "MSPM0G3107SRHBR", 0xAB39, 0xB7 },
|
|
{ "MSPM0G3505SDGS28R", 0xc504, 0x8e },
|
|
{ "MSPM0G3505SPMR", 0xc504, 0x1d },
|
|
{ "MSPM0G3505SPTR", 0xc504, 0x93 },
|
|
{ "MSPM0G3505SRGZR", 0xc504, 0xc7 },
|
|
{ "MSPM0G3505SRHBR", 0xc504, 0xe7 },
|
|
{ "MSPM0G3505TDGS28R", 0xc504, 0xdf },
|
|
{ "MSPM0G3506SDGS28R", 0x151f, 0x8 },
|
|
{ "MSPM0G3506SPMR", 0x151f, 0xd4 },
|
|
{ "MSPM0G3506SPTR", 0x151f, 0x39 },
|
|
{ "MSPM0G3506SRGZR", 0x151f, 0xfe },
|
|
{ "MSPM0G3506SRHBR", 0x151f, 0xb5 },
|
|
{ "MSPM0G3507SDGS28R", 0xae2d, 0xca },
|
|
{ "MSPM0G3507SPMR", 0xae2d, 0xc7 },
|
|
{ "MSPM0G3507SPTR", 0xae2d, 0x3f },
|
|
{ "MSPM0G3507SRGZR", 0xae2d, 0xf7 },
|
|
{ "MSPM0G3507SRHBR", 0xae2d, 0x4c },
|
|
{ "M0G3107QPMRQ1", 0x4e2f, 0x51 },
|
|
{ "M0G3107QPTRQ1", 0x4e2f, 0xc7},
|
|
{ "M0G3107QRGZRQ1", 0x4e2f, 0x8a },
|
|
{ "M0G3107QRHBRQ1", 0x4e2f, 0x9a},
|
|
{ "M0G3107QDGS28RQ1", 0x4e2f, 0xd5},
|
|
{ "M0G3107QDGS28RQ1", 0x4e2f, 0x67},
|
|
{ "M0G3107QDGS20RQ1", 0x4e2f, 0xfd},
|
|
{ "M0G3106QPMRQ1", 0x54C7, 0x08},
|
|
{ "M0G3105QDGS32RQ1", 0x1349, 0x08},
|
|
{ "M0G3106QPTRQ1", 0x54C7, 0x3F},
|
|
{ "M0G3105QDGS28RQ1", 0x1349, 0x1B},
|
|
{ "M0G3106QRGZRQ1", 0x94AD, 0xE6},
|
|
{ "M0G3105QDGS20RQ1", 0x1349, 0xFB},
|
|
{ "M0G3106QRHBRQ1", 0x94AD, 0x20},
|
|
{ "M0G3106QDGS32RQ1", 0x94AD, 0x8D},
|
|
{ "M0G3106QDGS28RQ1", 0x94AD, 0x03},
|
|
{ "M0G3106QDGS20RQ1", 0x94AD, 0x6F},
|
|
{ "M0G3105QPMRQ1", 0x1349, 0xD0},
|
|
{ "M0G3105QPTRQ1", 0x1349, 0xEF},
|
|
{ "M0G3105QRGZRQ1", 0x1349, 0x70},
|
|
{ "M0G3105QRHBRQ1", 0x1349, 0x01},
|
|
};
|
|
|
|
/* https://www.ti.com/lit/gpn/mspm0c1104 Table 8-12 and so on */
|
|
static const struct mspm0_part_info mspm0c_parts[] = {
|
|
{ "MSPS003F4SPW20R", 0x57b3, 0x70},
|
|
{ "MSPM0C1104SDGS20R", 0x57b3, 0x71},
|
|
{ "MSPM0C1104SRUKR", 0x57b3, 0x73},
|
|
{ "MSPM0C1104SDYYR", 0x57b3, 0x75},
|
|
{ "MSPM0C1104SDDFR", 0x57b3, 0x77},
|
|
{ "MSPM0C1104SDSGR", 0x57b3, 0x79},
|
|
};
|
|
|
|
/* https://www.ti.com/lit/gpn/MSPM0L2228 Table 8-16 and so on */
|
|
static const struct mspm0_part_info mspm0lx22x_parts[] = {
|
|
{ "MSPM0L1227SRGER", 0x7C32, 0xF1},
|
|
{ "MSPM0L1227SPTR", 0x7C32, 0xC9},
|
|
{ "MSPM0L1227SPMR", 0x7C32, 0x1C},
|
|
{ "MSPM0L1227SPNAR", 0x7C32, 0x91},
|
|
{ "MSPM0L1227SPNR", 0x7C32, 0x39},
|
|
{ "MSPM0L1228SRGER", 0x33F7, 0x13},
|
|
{ "MSPM0L1228SRHBR", 0x33F7, 0x3A},
|
|
{ "MSPM0L1228SRGZR", 0x33F7, 0xBC},
|
|
{ "MSPM0L1228SPTR", 0x33F7, 0xF8},
|
|
{ "MSPM0L1228SPMR", 0x33F7, 0xCE},
|
|
{ "MSPM0L1228SPNAR", 0x33F7, 0x59},
|
|
{ "MSPM0L1228SPNR", 0x33F7, 0x7},
|
|
{ "MSPM0L2227SRGZR", 0x5E8F, 0x90},
|
|
{ "MSPM0L2227SPTR", 0x5E8F, 0xA},
|
|
{ "MSPM0L2227SPMR", 0x5E8F, 0x6D},
|
|
{ "MSPM0L2227SPNAR", 0x5E8F, 0x24},
|
|
{ "MSPM0L2227SPNR", 0x5E8F, 0x68},
|
|
{ "MSPM0L2228SRGZR", 0x2C38, 0xB8},
|
|
{ "MSPM0L2228SPTR", 0x2C38, 0x25},
|
|
{ "MSPM0L2228SPMR", 0x2C38, 0x6E},
|
|
{ "MSPM0L2228SPNAR", 0x2C38, 0x63},
|
|
{ "MSPM0L2228SPNR", 0x2C38, 0x3C},
|
|
};
|
|
|
|
static const struct mspm0_family_info mspm0_finf[] = {
|
|
{ "MSPM0L", 0xbb82, ARRAY_SIZE(mspm0l_parts), mspm0l_parts },
|
|
{ "MSPM0Lx22x", 0xbb9f, ARRAY_SIZE(mspm0lx22x_parts), mspm0lx22x_parts },
|
|
{ "MSPM0G", 0xbb88, ARRAY_SIZE(mspm0g_parts), mspm0g_parts },
|
|
{ "MSPM0C", 0xbba1, ARRAY_SIZE(mspm0c_parts), mspm0c_parts },
|
|
};
|
|
|
|
/*
|
|
* OpenOCD command interface
|
|
*/
|
|
|
|
/*
|
|
* flash_bank mspm0 <base> <size> 0 0 <target#>
|
|
*/
|
|
FLASH_BANK_COMMAND_HANDLER(mspm0_flash_bank_command)
|
|
{
|
|
struct mspm0_flash_bank *mspm0_info;
|
|
|
|
switch (bank->base) {
|
|
case MSPM0_FLASH_BASE_NONMAIN:
|
|
case MSPM0_FLASH_BASE_MAIN:
|
|
case MSPM0_FLASH_BASE_DATA:
|
|
break;
|
|
default:
|
|
LOG_ERROR("Invalid bank address " TARGET_ADDR_FMT, bank->base);
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
mspm0_info = calloc(1, sizeof(struct mspm0_flash_bank));
|
|
if (!mspm0_info) {
|
|
LOG_ERROR("%s: Out of memory for mspm0_info!", __func__);
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
bank->driver_priv = mspm0_info;
|
|
|
|
mspm0_info->sector_size = 0x400;
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
/*
|
|
* Chip identification and status
|
|
*/
|
|
static int get_mspm0_info(struct flash_bank *bank, struct command_invocation *cmd)
|
|
{
|
|
struct mspm0_flash_bank *mspm0_info = bank->driver_priv;
|
|
|
|
if (mspm0_info->did == 0)
|
|
return ERROR_FLASH_BANK_NOT_PROBED;
|
|
|
|
command_print_sameline(cmd,
|
|
"\nTI MSPM0 information: Chip is "
|
|
"%s rev %d Device Unique ID: 0x%" PRIu32 "\n",
|
|
mspm0_info->name, mspm0_info->version,
|
|
mspm0_info->traceid);
|
|
command_print_sameline(cmd,
|
|
"main flash: %uKiB in %u bank(s), sram: %uKiB, data flash: %uKiB",
|
|
mspm0_info->main_flash_size_kb,
|
|
mspm0_info->main_flash_num_banks, mspm0_info->sram_size_kb,
|
|
mspm0_info->data_flash_size_kb);
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
/* Extract a bitfield helper */
|
|
static unsigned int mspm0_extract_val(unsigned int var, unsigned char hi, unsigned char lo)
|
|
{
|
|
return (var & GENMASK(hi, lo)) >> lo;
|
|
}
|
|
|
|
static int mspm0_read_part_info(struct flash_bank *bank)
|
|
{
|
|
struct mspm0_flash_bank *mspm0_info = bank->driver_priv;
|
|
struct target *target = bank->target;
|
|
const struct mspm0_family_info *minfo = NULL;
|
|
|
|
/* Read and parse chip identification and flash version register */
|
|
uint32_t did;
|
|
int retval = target_read_u32(target, MSPM0_DID, &did);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Failed to read device ID");
|
|
return retval;
|
|
}
|
|
retval = target_read_u32(target, MSPM0_TRACEID, &mspm0_info->traceid);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Failed to read trace ID");
|
|
return retval;
|
|
}
|
|
uint32_t userid;
|
|
retval = target_read_u32(target, MSPM0_USERID, &userid);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Failed to read user ID");
|
|
return retval;
|
|
}
|
|
uint32_t flashram;
|
|
retval = target_read_u32(target, MSPM0_SRAMFLASH, &flashram);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Failed to read sramflash register");
|
|
return retval;
|
|
}
|
|
uint32_t flashdesc;
|
|
retval = target_read_u32(target, FCTL_REG_DESC, &flashdesc);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Failed to read flashctl description register");
|
|
return retval;
|
|
}
|
|
|
|
unsigned char version = mspm0_extract_val(did, 31, 28);
|
|
unsigned short pnum = mspm0_extract_val(did, 27, 12);
|
|
unsigned char variant = mspm0_extract_val(userid, 23, 16);
|
|
unsigned short part = mspm0_extract_val(userid, 15, 0);
|
|
unsigned short manufacturer = mspm0_extract_val(did, 11, 1);
|
|
|
|
/*
|
|
* Valid DIE and manufacturer ID?
|
|
* Check the ALWAYS_1 bit to be 1 and manufacturer to be 0x17. All MSPM0
|
|
* devices within the Device ID field of the factory constants will
|
|
* always read 0x17 as it is TI's JEDEC bank and company code. If 1
|
|
* and 0x17 is not read from their respective registers then it truly
|
|
* is not a MSPM0 device so we will return an error instead of
|
|
* going any further.
|
|
*/
|
|
if (!(did & BIT(0)) || !(manufacturer & TI_MANUFACTURER_ID)) {
|
|
LOG_WARNING("Unknown Device ID[0x%" PRIx32 "], cannot identify target",
|
|
did);
|
|
LOG_DEBUG("did 0x%" PRIx32 ", traceid 0x%" PRIx32 ", userid 0x%" PRIx32
|
|
", flashram 0x%" PRIx32 "", did, mspm0_info->traceid, userid,
|
|
flashram);
|
|
return ERROR_FLASH_OPERATION_FAILED;
|
|
}
|
|
|
|
/* Initialize master index selector and probe status*/
|
|
unsigned char minfo_idx = 0xff;
|
|
unsigned char probe_status = MSPM0_NO_ID_FOUND;
|
|
|
|
/* Check if we at least know the family of devices */
|
|
for (unsigned int i = 0; i < ARRAY_SIZE(mspm0_finf); i++) {
|
|
if (mspm0_finf[i].part_num == pnum) {
|
|
minfo_idx = i;
|
|
minfo = &mspm0_finf[i];
|
|
probe_status = MSPM0_DEV_ID_FOUND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Initialize part index selector*/
|
|
unsigned char pinfo_idx = 0xff;
|
|
|
|
/*
|
|
* If we can identify the part number then we will attempt to identify
|
|
* the specific chip. Otherwise, if we do not know the part number then
|
|
* it would be useless to identify the specific chip.
|
|
*/
|
|
if (probe_status == MSPM0_DEV_ID_FOUND) {
|
|
/* Can we specifically identify the chip */
|
|
for (unsigned int i = 0; i < minfo->part_count; i++) {
|
|
if (minfo->part_info[i].part == part
|
|
&& minfo->part_info[i].variant == variant) {
|
|
pinfo_idx = i;
|
|
probe_status = MSPM0_DEV_PART_ID_FOUND;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We will check the status of our probe within this switch-case statement
|
|
* using these three scenarios.
|
|
*
|
|
* 1) Device, part, and variant ID is unknown.
|
|
* 2) Device ID is known but the part/variant ID is unknown.
|
|
* 3) Device ID and part/variant ID is known
|
|
*
|
|
* For scenario 1, we allow the user to continue because if the
|
|
* manufacturer matches TI's JEDEC value and ALWAYS_1 from the device ID
|
|
* field is correct then the assumption the user is using an MSPM0 device
|
|
* can be made.
|
|
*/
|
|
switch (probe_status) {
|
|
case MSPM0_NO_ID_FOUND:
|
|
mspm0_info->name = "mspm0x";
|
|
LOG_INFO("Unidentified PART[0x%x]/variant[0x%x"
|
|
"], unknown DeviceID[0x%x"
|
|
"]. Attempting to proceed as %s.", part, variant, pnum,
|
|
mspm0_info->name);
|
|
break;
|
|
case MSPM0_DEV_ID_FOUND:
|
|
mspm0_info->name = mspm0_finf[minfo_idx].family_name;
|
|
LOG_INFO("Unidentified PART[0x%x]/variant[0x%x"
|
|
"], known DeviceID[0x%x"
|
|
"]. Attempting to proceed as %s.", part, variant, pnum,
|
|
mspm0_info->name);
|
|
break;
|
|
case MSPM0_DEV_PART_ID_FOUND:
|
|
default:
|
|
mspm0_info->name = mspm0_finf[minfo_idx].part_info[pinfo_idx].part_name;
|
|
LOG_DEBUG("Part: %s detected", mspm0_info->name);
|
|
break;
|
|
}
|
|
|
|
mspm0_info->did = did;
|
|
mspm0_info->version = version;
|
|
mspm0_info->data_flash_size_kb = mspm0_extract_val(flashram, 31, 26);
|
|
mspm0_info->main_flash_size_kb = mspm0_extract_val(flashram, 11, 0);
|
|
mspm0_info->main_flash_num_banks = mspm0_extract_val(flashram, 13, 12) + 1;
|
|
mspm0_info->sram_size_kb = mspm0_extract_val(flashram, 25, 16);
|
|
mspm0_info->flash_version = mspm0_extract_val(flashdesc, 15, 12);
|
|
|
|
/*
|
|
* Hardcode flash_word_size unless we find some other pattern
|
|
* See section 7.7 (Foot note mentions the flash word size).
|
|
* almost all values seem to be 8 bytes, but if there are variance,
|
|
* then we should update mspm0_part_info structure with this info.
|
|
*/
|
|
mspm0_info->flash_word_size_bytes = 8;
|
|
|
|
LOG_DEBUG("Detected: main flash: %uKb in %u banks, sram: %uKb, data flash: %uKb",
|
|
mspm0_info->main_flash_size_kb, mspm0_info->main_flash_num_banks,
|
|
mspm0_info->sram_size_kb, mspm0_info->data_flash_size_kb);
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
/*
|
|
* Decode error values
|
|
*/
|
|
static const struct {
|
|
const unsigned char bit_offset;
|
|
const char *fail_string;
|
|
} mspm0_fctl_fail_decode_strings[] = {
|
|
{ 2, "CMDINPROGRESS" },
|
|
{ 4, "FAILWEPROT" },
|
|
{ 5, "FAILVERIFY" },
|
|
{ 6, "FAILILLADDR" },
|
|
{ 7, "FAILMODE" },
|
|
{ 12, "FAILMISC" },
|
|
};
|
|
|
|
static const char *mspm0_fctl_translate_ret_err(unsigned int return_code)
|
|
{
|
|
for (unsigned int i = 0; i < ARRAY_SIZE(mspm0_fctl_fail_decode_strings); i++) {
|
|
if (return_code & BIT(mspm0_fctl_fail_decode_strings[i].bit_offset))
|
|
return mspm0_fctl_fail_decode_strings[i].fail_string;
|
|
}
|
|
|
|
/* If unknown error notify the user*/
|
|
return "FAILUNKNOWN";
|
|
}
|
|
|
|
static int mspm0_fctl_get_sector_reg(struct flash_bank *bank, unsigned int addr,
|
|
unsigned int *reg, unsigned int *sector_mask)
|
|
{
|
|
struct mspm0_flash_bank *mspm0_info = bank->driver_priv;
|
|
struct target *target = bank->target;
|
|
int ret = ERROR_OK;
|
|
unsigned int sector_num = (addr >> 10);
|
|
unsigned int sector_in_bank = sector_num;
|
|
unsigned int phys_sector_num = sector_num;
|
|
uint32_t sysctl_sec_status;
|
|
unsigned int exec_upper_bank;
|
|
|
|
/*
|
|
* If the device has dual banks we will need to check if it is configured
|
|
* to execute from the upper bank. In the scenario that we are executing
|
|
* from upper bank then we will need to protect it using CMDWEPROTA rather
|
|
* than CMDWEPROTB. We also need to take into account what sector
|
|
* we're using when going between banks.
|
|
*/
|
|
if (mspm0_info->main_flash_num_banks > 1 &&
|
|
bank->base == MSPM0_FLASH_BASE_MAIN) {
|
|
ret = target_read_u32(target, SYSCTL_SECCFG_SECSTATUS, &sysctl_sec_status);
|
|
if (ret != ERROR_OK)
|
|
return ret;
|
|
exec_upper_bank = mspm0_extract_val(sysctl_sec_status, 12, 12);
|
|
if (exec_upper_bank) {
|
|
if (sector_num > (mspm0_info->main_flash_size_kb / 2)) {
|
|
phys_sector_num =
|
|
sector_num - (mspm0_info->main_flash_size_kb / 2);
|
|
} else {
|
|
phys_sector_num =
|
|
sector_num + (mspm0_info->main_flash_size_kb / 2);
|
|
}
|
|
}
|
|
sector_in_bank =
|
|
sector_num % (mspm0_info->main_flash_size_kb /
|
|
mspm0_info->main_flash_num_banks);
|
|
}
|
|
|
|
/*
|
|
* NOTE: MSPM0 devices of version A will use CMDWEPROTA and CMDWEPROTB
|
|
* for MAIN flash. CMDWEPROTC is included in the TRM/DATASHEET but for
|
|
* all practical purposes, it is considered reserved. If the flash
|
|
* version on the device is version B, then we will only use
|
|
* CMDWEPROTB for MAIN and DATA flash if the device has it.
|
|
*/
|
|
switch (bank->base) {
|
|
case MSPM0_FLASH_BASE_MAIN:
|
|
case MSPM0_FLASH_BASE_DATA:
|
|
if (mspm0_info->flash_version < FCTL_FEATURE_VER_B) {
|
|
/* Use CMDWEPROTA */
|
|
if (phys_sector_num < 32) {
|
|
*sector_mask = BIT(phys_sector_num);
|
|
*reg = FCTL_REG_CMDWEPROTA;
|
|
}
|
|
|
|
/* Use CMDWEPROTB */
|
|
if (phys_sector_num >= 32 && sector_in_bank < 256) {
|
|
/* Dual bank system */
|
|
if (mspm0_info->main_flash_num_banks > 1)
|
|
*sector_mask = BIT(sector_in_bank / 8);
|
|
else /* Single bank system */
|
|
*sector_mask = BIT((sector_in_bank - 32) / 8);
|
|
*reg = FCTL_REG_CMDWEPROTB;
|
|
}
|
|
} else {
|
|
*sector_mask = BIT((sector_in_bank / 8) % 32);
|
|
*reg = FCTL_REG_CMDWEPROTB;
|
|
}
|
|
break;
|
|
case MSPM0_FLASH_BASE_NONMAIN:
|
|
*sector_mask = BIT(sector_num % 32);
|
|
*reg = FCTL_REG_CMDWEPROTNM;
|
|
break;
|
|
default:
|
|
/*
|
|
* Not expected to reach here due to check in mspm0_address_check()
|
|
* but adding it as another layer of safety.
|
|
*/
|
|
ret = ERROR_FLASH_DST_OUT_OF_BANK;
|
|
break;
|
|
}
|
|
|
|
if (ret != ERROR_OK)
|
|
LOG_ERROR("Unable to map sector protect reg for address 0x%08x", addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mspm0_address_check(struct flash_bank *bank, unsigned int addr)
|
|
{
|
|
struct mspm0_flash_bank *mspm0_info = bank->driver_priv;
|
|
unsigned int flash_main_size = mspm0_info->main_flash_size_kb * 1024;
|
|
unsigned int flash_data_size = mspm0_info->data_flash_size_kb * 1024;
|
|
int ret = ERROR_FLASH_SECTOR_INVALID;
|
|
|
|
/*
|
|
* Before unprotecting any memory lets make sure that the address and
|
|
* bank given is a known bank and whether or not the address falls under
|
|
* the proper bank.
|
|
*/
|
|
switch (bank->base) {
|
|
case MSPM0_FLASH_BASE_MAIN:
|
|
if (addr <= (MSPM0_FLASH_BASE_MAIN + flash_main_size))
|
|
ret = ERROR_OK;
|
|
break;
|
|
case MSPM0_FLASH_BASE_NONMAIN:
|
|
if (addr >= MSPM0_FLASH_BASE_NONMAIN && addr <= MSPM0_FLASH_END_NONMAIN)
|
|
ret = ERROR_OK;
|
|
break;
|
|
case MSPM0_FLASH_BASE_DATA:
|
|
if (addr >= MSPM0_FLASH_BASE_DATA &&
|
|
addr <= (MSPM0_FLASH_BASE_DATA + flash_data_size))
|
|
ret = ERROR_OK;
|
|
break;
|
|
default:
|
|
ret = ERROR_FLASH_DST_OUT_OF_BANK;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mspm0_fctl_unprotect_sector(struct flash_bank *bank, unsigned int addr)
|
|
{
|
|
struct target *target = bank->target;
|
|
unsigned int reg = 0x0;
|
|
uint32_t sector_mask = 0x0;
|
|
int ret;
|
|
|
|
ret = mspm0_address_check(bank, addr);
|
|
switch (ret) {
|
|
case ERROR_FLASH_SECTOR_INVALID:
|
|
LOG_ERROR("Unable to map sector protect reg for address 0x%08x", addr);
|
|
break;
|
|
case ERROR_FLASH_DST_OUT_OF_BANK:
|
|
LOG_ERROR("Unable to determine which bank to use 0x%08x", addr);
|
|
break;
|
|
default:
|
|
mspm0_fctl_get_sector_reg(bank, addr, ®, §or_mask);
|
|
ret = target_write_u32(target, reg, ~sector_mask);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mspm0_fctl_cfg_command(struct flash_bank *bank,
|
|
uint32_t addr,
|
|
uint32_t cmd,
|
|
uint32_t byte_en)
|
|
{
|
|
struct target *target = bank->target;
|
|
|
|
/*
|
|
* Configure the flash operation within the CMDTYPE register, byte_en
|
|
* bits if needed, and then set the address where the flash operation
|
|
* will execute.
|
|
*/
|
|
int retval = target_write_u32(target, FCTL_REG_CMDTYPE, cmd);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
if (byte_en != 0) {
|
|
retval = target_write_u32(target, FCTL_REG_CMDBYTEN, byte_en);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
}
|
|
|
|
return target_write_u32(target, FCTL_REG_CMDADDR, addr);
|
|
}
|
|
|
|
static int mspm0_fctl_wait_cmd_ok(struct flash_bank *bank)
|
|
{
|
|
struct target *target = bank->target;
|
|
uint32_t return_code = 0;
|
|
int64_t start_ms;
|
|
int64_t elapsed_ms;
|
|
|
|
start_ms = timeval_ms();
|
|
while ((return_code & FCTL_STATCMD_CMDDONE_MASK) != FCTL_STATCMD_CMDDONE_STATDONE) {
|
|
int retval = target_read_u32(target, FCTL_REG_STATCMD, &return_code);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
elapsed_ms = timeval_ms() - start_ms;
|
|
if (elapsed_ms > MSPM0_FLASH_TIMEOUT_MS)
|
|
break;
|
|
|
|
keep_alive();
|
|
}
|
|
|
|
if ((return_code & FCTL_STATCMD_CMDPASS_MASK) != FCTL_STATCMD_CMDPASS_STATPASS) {
|
|
LOG_ERROR("Flash command failed: %s", mspm0_fctl_translate_ret_err(return_code));
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int mspm0_fctl_sector_erase(struct flash_bank *bank, uint32_t addr)
|
|
{
|
|
struct target *target = bank->target;
|
|
|
|
/*
|
|
* TRM Says:
|
|
* Note that the CMDWEPROTx registers are reset to a protected state
|
|
* at the end of all program and erase operations. These registers
|
|
* must be re-configured by software before a new operation is
|
|
* initiated.
|
|
*
|
|
* This means that as we start erasing sector by sector, the protection
|
|
* registers are reset and need to be unprotected *again* for the next
|
|
* erase operation. Unfortunately, this means that we cannot do a unitary
|
|
* unprotect operation independent of flash erase operation
|
|
*/
|
|
int retval = mspm0_fctl_unprotect_sector(bank, addr);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Unprotecting sector of memory at address 0x%08" PRIx32
|
|
" failed", addr);
|
|
return retval;
|
|
}
|
|
|
|
/* Actual erase operation */
|
|
retval = mspm0_fctl_cfg_command(bank, addr,
|
|
(FCTL_CMDTYPE_COMMAND_ERASE | FCTL_CMDTYPE_SIZE_SECTOR), 0);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
retval = target_write_u32(target, FCTL_REG_CMDEXEC, FCTL_CMDEXEC_VAL_EXECUTE);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
return mspm0_fctl_wait_cmd_ok(bank);
|
|
}
|
|
|
|
static int mspm0_protect_check(struct flash_bank *bank)
|
|
{
|
|
struct mspm0_flash_bank *mspm0_info = bank->driver_priv;
|
|
|
|
if (mspm0_info->did == 0)
|
|
return ERROR_FLASH_BANK_NOT_PROBED;
|
|
|
|
/*
|
|
* TRM Says:
|
|
* Note that the CMDWEPROTx registers are reset to a protected state
|
|
* at the end of all program and erase operations. These registers
|
|
* must be re-configured by software before a new operation is
|
|
* initiated.
|
|
*
|
|
* This means that when any flash operation is performed at a block level,
|
|
* the block is locked back again. This prevents usage where we can set a
|
|
* protection level once at the flash level and then do erase / write
|
|
* operation without touching the protection register (since it is
|
|
* reset by hardware automatically). In effect, we cannot use the hardware
|
|
* defined protection scheme in openOCD.
|
|
*
|
|
* To deal with this protection scheme, the CMDWEPROTx register that
|
|
* correlates to the sector is modified at the time of operation and as far
|
|
* openOCD is concerned, the flash operates as completely un-protected
|
|
* flash.
|
|
*/
|
|
for (unsigned int i = 0; i < bank->num_sectors; i++)
|
|
bank->sectors[i].is_protected = 0;
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int mspm0_erase(struct flash_bank *bank, unsigned int first, unsigned int last)
|
|
{
|
|
struct target *target = bank->target;
|
|
struct mspm0_flash_bank *mspm0_info = bank->driver_priv;
|
|
int retval = ERROR_OK;
|
|
uint32_t protect_reg_cache[MSPM0_MAX_PROTREGS];
|
|
|
|
if (bank->target->state != TARGET_HALTED) {
|
|
LOG_ERROR("Please halt target for erasing flash");
|
|
return ERROR_TARGET_NOT_HALTED;
|
|
}
|
|
|
|
if (mspm0_info->did == 0)
|
|
return ERROR_FLASH_BANK_NOT_PROBED;
|
|
|
|
/* Pick a copy of the current protection config for later restoration */
|
|
for (unsigned int i = 0; i < mspm0_info->protect_reg_count; i++) {
|
|
retval = target_read_u32(target,
|
|
mspm0_info->protect_reg_base + (i * 4),
|
|
&protect_reg_cache[i]);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Failed saving flashctl protection status");
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
switch (bank->base) {
|
|
case MSPM0_FLASH_BASE_MAIN:
|
|
for (unsigned int csa = first; csa <= last; csa++) {
|
|
unsigned int addr = csa * mspm0_info->sector_size;
|
|
retval = mspm0_fctl_sector_erase(bank, addr);
|
|
if (retval != ERROR_OK)
|
|
LOG_ERROR("Sector erase on MAIN failed at address 0x%08x "
|
|
"(sector: %u)", addr, csa);
|
|
}
|
|
break;
|
|
case MSPM0_FLASH_BASE_NONMAIN:
|
|
retval = mspm0_fctl_sector_erase(bank, MSPM0_FLASH_BASE_NONMAIN);
|
|
if (retval != ERROR_OK)
|
|
LOG_ERROR("Sector erase on NONMAIN failed");
|
|
break;
|
|
case MSPM0_FLASH_BASE_DATA:
|
|
for (unsigned int csa = first; csa <= last; csa++) {
|
|
unsigned int addr = (MSPM0_FLASH_BASE_DATA +
|
|
(csa * mspm0_info->sector_size));
|
|
retval = mspm0_fctl_sector_erase(bank, addr);
|
|
if (retval != ERROR_OK)
|
|
LOG_ERROR("Sector erase on DATA bank failed at address 0x%08x "
|
|
"(sector: %u)", addr, csa);
|
|
}
|
|
break;
|
|
default:
|
|
LOG_ERROR("Invalid memory region access");
|
|
retval = ERROR_FLASH_BANK_INVALID;
|
|
break;
|
|
}
|
|
|
|
/* If there were any issues in our checks, return the error */
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
/*
|
|
* TRM Says:
|
|
* Note that the CMDWEPROTx registers are reset to a protected state
|
|
* at the end of all program and erase operations. These registers
|
|
* must be re-configured by software before a new operation is
|
|
* initiated
|
|
* Let us just Dump the protection registers back to the system.
|
|
* That way we retain the protection status as requested by the user
|
|
*/
|
|
for (unsigned int i = 0; i < mspm0_info->protect_reg_count; i++) {
|
|
retval = target_write_u32(target, mspm0_info->protect_reg_base + (i * 4),
|
|
protect_reg_cache[i]);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Failed re-applying protection status of flashctl");
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int mspm0_write(struct flash_bank *bank, const unsigned char *buffer,
|
|
unsigned int offset, unsigned int count)
|
|
{
|
|
struct target *target = bank->target;
|
|
struct mspm0_flash_bank *mspm0_info = bank->driver_priv;
|
|
uint32_t protect_reg_cache[MSPM0_MAX_PROTREGS];
|
|
int retval;
|
|
|
|
/*
|
|
* XXX: TRM Says:
|
|
* The number of program operations applied to a given word line must be
|
|
* monitored to ensure that the maximum word line program limit before
|
|
* erase is not violated.
|
|
*
|
|
* There is no reasonable way we can maintain that state in OpenOCD. So,
|
|
* Let the manufacturing path figure this out.
|
|
*/
|
|
|
|
if (bank->target->state != TARGET_HALTED) {
|
|
LOG_ERROR("Please halt target for programming flash");
|
|
return ERROR_TARGET_NOT_HALTED;
|
|
}
|
|
|
|
if (mspm0_info->did == 0)
|
|
return ERROR_FLASH_BANK_NOT_PROBED;
|
|
|
|
/*
|
|
* Pick a copy of the current protection config for later restoration
|
|
* We need to restore these regs after every write, so instead of trying
|
|
* to figure things out on the fly, we just context save and restore
|
|
*/
|
|
for (unsigned int i = 0; i < mspm0_info->protect_reg_count; i++) {
|
|
retval = target_read_u32(target,
|
|
mspm0_info->protect_reg_base + (i * 4),
|
|
&protect_reg_cache[i]);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Failed saving flashctl protection status");
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
/* Add proper memory offset for bank being written to */
|
|
unsigned int addr = bank->base + offset;
|
|
|
|
while (count) {
|
|
unsigned int num_bytes_to_write;
|
|
uint32_t bytes_en;
|
|
|
|
/*
|
|
* If count is not 64 bit aligned, we will do byte wise op to keep things simple
|
|
* Usually this might mean we need to additional write ops towards
|
|
* trailing edge, but that is a tiny penalty for image downloads.
|
|
* NOTE: we are going to assume the device does not support multi-word
|
|
* programming - there does not seem to be discoverability!
|
|
*/
|
|
if (count < mspm0_info->flash_word_size_bytes)
|
|
num_bytes_to_write = count;
|
|
else
|
|
num_bytes_to_write = mspm0_info->flash_word_size_bytes;
|
|
|
|
/* Data bytes to write */
|
|
bytes_en = (1 << num_bytes_to_write) - 1;
|
|
/* ECC chunks to write */
|
|
switch (mspm0_info->flash_word_size_bytes) {
|
|
case 8:
|
|
bytes_en |= BIT(8);
|
|
break;
|
|
case 16:
|
|
bytes_en |= BIT(16);
|
|
bytes_en |= (num_bytes_to_write > 8) ? BIT(17) : 0;
|
|
break;
|
|
default:
|
|
LOG_ERROR("Invalid flash_word_size_bytes %d",
|
|
mspm0_info->flash_word_size_bytes);
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
retval = mspm0_fctl_cfg_command(bank, addr,
|
|
(FCTL_CMDTYPE_COMMAND_PROGRAM | FCTL_CMDTYPE_SIZE_ONEWORD),
|
|
bytes_en);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
retval = mspm0_fctl_unprotect_sector(bank, addr);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
retval = target_write_buffer(target, FCTL_REG_CMDDATA0, num_bytes_to_write, buffer);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
addr += num_bytes_to_write;
|
|
buffer += num_bytes_to_write;
|
|
count -= num_bytes_to_write;
|
|
|
|
retval = target_write_u32(target, FCTL_REG_CMDEXEC, FCTL_CMDEXEC_VAL_EXECUTE);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
retval = mspm0_fctl_wait_cmd_ok(bank);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* TRM Says:
|
|
* Note that the CMDWEPROTx registers are reset to a protected state
|
|
* at the end of all program and erase operations. These registers
|
|
* must be re-configured by software before a new operation is
|
|
* initiated
|
|
* Let us just Dump the protection registers back to the system.
|
|
* That way we retain the protection status as requested by the user
|
|
*/
|
|
for (unsigned int i = 0; i < mspm0_info->protect_reg_count; i++) {
|
|
retval = target_write_u32(target,
|
|
mspm0_info->protect_reg_base + (i * 4),
|
|
protect_reg_cache[i]);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Failed re-applying protection status of flashctl");
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int mspm0_probe(struct flash_bank *bank)
|
|
{
|
|
struct mspm0_flash_bank *mspm0_info = bank->driver_priv;
|
|
|
|
/*
|
|
* If this is a mspm0 chip, it has flash; probe() is just
|
|
* to figure out how much is present. Only do it once.
|
|
*/
|
|
if (mspm0_info->did != 0)
|
|
return ERROR_OK;
|
|
|
|
/*
|
|
* mspm0_read_part_info() already handled error checking and
|
|
* reporting. Note that it doesn't write, so we don't care about
|
|
* whether the target is halted or not.
|
|
*/
|
|
int retval = mspm0_read_part_info(bank);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
if (bank->sectors) {
|
|
free(bank->sectors);
|
|
bank->sectors = NULL;
|
|
}
|
|
|
|
bank->write_start_alignment = 4;
|
|
bank->write_end_alignment = 4;
|
|
|
|
switch (bank->base) {
|
|
case MSPM0_FLASH_BASE_NONMAIN:
|
|
bank->size = 1024;
|
|
bank->num_sectors = 0x1;
|
|
mspm0_info->protect_reg_base = FCTL_REG_CMDWEPROTNM;
|
|
mspm0_info->protect_reg_count = 1;
|
|
break;
|
|
case MSPM0_FLASH_BASE_MAIN:
|
|
bank->size = (mspm0_info->main_flash_size_kb * 1024);
|
|
bank->num_sectors = bank->size / mspm0_info->sector_size;
|
|
/*
|
|
* If the feature version bit read from the FCTL_REG_DESC is
|
|
* greater than or equal to 0xA then it means that the device
|
|
* will exclusively use CMDWEPROTB ONLY for MAIN memory protection
|
|
*/
|
|
if (mspm0_info->flash_version >= FCTL_FEATURE_VER_B) {
|
|
mspm0_info->protect_reg_base = FCTL_REG_CMDWEPROTB;
|
|
mspm0_info->protect_reg_count = 1;
|
|
} else {
|
|
mspm0_info->protect_reg_base = FCTL_REG_CMDWEPROTA;
|
|
mspm0_info->protect_reg_count = 3;
|
|
}
|
|
break;
|
|
case MSPM0_FLASH_BASE_DATA:
|
|
if (!mspm0_info->data_flash_size_kb) {
|
|
LOG_INFO("Data region NOT available!");
|
|
bank->size = 0x0;
|
|
bank->num_sectors = 0x0;
|
|
return ERROR_OK;
|
|
}
|
|
/*
|
|
* Any MSPM0 device containing data bank will have a flashctl
|
|
* feature version of 0xA or higher. Since data bank is treated
|
|
* like MAIN memory, it will also exclusively use CMDWEPROTB for
|
|
* protection.
|
|
*/
|
|
bank->size = (mspm0_info->data_flash_size_kb * 1024);
|
|
bank->num_sectors = bank->size / mspm0_info->sector_size;
|
|
mspm0_info->protect_reg_base = FCTL_REG_CMDWEPROTB;
|
|
mspm0_info->protect_reg_count = 1;
|
|
break;
|
|
default:
|
|
LOG_ERROR("Invalid bank address " TARGET_ADDR_FMT,
|
|
bank->base);
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector));
|
|
if (!bank->sectors) {
|
|
LOG_ERROR("Out of memory for sectors!");
|
|
return ERROR_FAIL;
|
|
}
|
|
for (unsigned int i = 0; i < bank->num_sectors; i++) {
|
|
bank->sectors[i].offset = i * mspm0_info->sector_size;
|
|
bank->sectors[i].size = mspm0_info->sector_size;
|
|
bank->sectors[i].is_erased = -1;
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
const struct flash_driver mspm0_flash = {
|
|
.name = "mspm0",
|
|
.flash_bank_command = mspm0_flash_bank_command,
|
|
.erase = mspm0_erase,
|
|
.protect = NULL,
|
|
.write = mspm0_write,
|
|
.read = default_flash_read,
|
|
.probe = mspm0_probe,
|
|
.auto_probe = mspm0_probe,
|
|
.erase_check = default_flash_blank_check,
|
|
.protect_check = mspm0_protect_check,
|
|
.info = get_mspm0_info,
|
|
.free_driver_priv = default_flash_free_driver_priv,
|
|
};
|