openocd/src/flash/nor/stellaris.c
Antonio Borneo 62329444ab flash: avoid checking for non NULL pointer to free it
The function free() can be called with a NULL pointer as argument,
no need to check the argument before. If the pointer is NULL, no
operation is performed by free().

Remove the occurrences of pattern:
	if (ptr)
		free(ptr);

There are cases where the pointer is set to NULL after free(), but
then re-assigned within few lines. Drop the setting to NULL when
this is evident. Anyway, the compiler will remove the useless
assignment so no reason to be too much aggressive in this change.

Change-Id: I55b2ce7cbe201410016398933e34d33a4b66e30b
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/5811
Tested-by: jenkins
2020-09-05 17:11:34 +01:00

1454 lines
43 KiB
C

/***************************************************************************
* Copyright (C) 2006 by Magnus Lundin *
* lundin@mlu.mine.nu *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* 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/>. *
***************************************************************************/
/***************************************************************************
* STELLARIS flash is tested on LM3S811, LM3S6965, LM3s3748, more.
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "jtag/interface.h"
#include "imp.h"
#include <target/algorithm.h>
#include <target/armv7m.h>
#define DID0_VER(did0) ((did0 >> 28)&0x07)
/* STELLARIS control registers */
#define SCB_BASE 0x400FE000
#define DID0 0x000
#define DID1 0x004
#define DC0 0x008
#define DC1 0x010
#define DC2 0x014
#define DC3 0x018
#define DC4 0x01C
#define RIS 0x050
#define RCC 0x060
#define PLLCFG 0x064
#define RCC2 0x070
#define NVMSTAT 0x1a0
/* "legacy" flash memory protection registers (64KB max) */
#define FMPRE 0x130
#define FMPPE 0x134
/* new flash memory protection registers (for more than 64KB) */
#define FMPRE0 0x200 /* PRE1 = PRE0 + 4, etc */
#define FMPPE0 0x400 /* PPE1 = PPE0 + 4, etc */
#define USECRL 0x140
#define FLASH_CONTROL_BASE 0x400FD000
#define FLASH_FMA (FLASH_CONTROL_BASE | 0x000)
#define FLASH_FMD (FLASH_CONTROL_BASE | 0x004)
#define FLASH_FMC (FLASH_CONTROL_BASE | 0x008)
#define FLASH_CRIS (FLASH_CONTROL_BASE | 0x00C)
#define FLASH_CIM (FLASH_CONTROL_BASE | 0x010)
#define FLASH_MISC (FLASH_CONTROL_BASE | 0x014)
#define FLASH_FSIZE (FLASH_CONTROL_BASE | 0xFC0)
#define FLASH_SSIZE (FLASH_CONTROL_BASE | 0xFC4)
#define AMISC 1
#define PMISC 2
#define AMASK 1
#define PMASK 2
/* Flash Controller Command bits */
#define FMC_WRKEY (0xA442 << 16)
#define FMC_COMT (1 << 3)
#define FMC_MERASE (1 << 2)
#define FMC_ERASE (1 << 1)
#define FMC_WRITE (1 << 0)
/* STELLARIS constants */
/* values to write in FMA to commit write-"once" values */
#define FLASH_FMA_PRE(x) (2 * (x)) /* for FMPPREx */
#define FLASH_FMA_PPE(x) (2 * (x) + 1) /* for FMPPPEx */
static void stellaris_read_clock_info(struct flash_bank *bank);
static int stellaris_mass_erase(struct flash_bank *bank);
struct stellaris_flash_bank {
/* chip id register */
uint32_t did0;
uint32_t did1;
uint32_t dc0;
uint32_t dc1;
uint32_t fsize;
uint32_t ssize;
const char *target_name;
uint8_t target_class;
uint32_t sramsiz;
/* flash geometry */
uint32_t num_pages;
uint32_t pagesize;
/* main clock status */
uint32_t rcc;
uint32_t rcc2;
uint8_t mck_valid;
uint8_t xtal_mask;
uint32_t iosc_freq;
uint32_t mck_freq;
const char *iosc_desc;
const char *mck_desc;
};
/* Autogenerated by contrib/gen-stellaris-part-header.pl */
/* From Stellaris Firmware Development Package revision 9453 */
static const struct {
uint8_t class;
uint8_t partno;
const char *partname;
} StellarisParts[] = {
{0x00, 0x01, "LM3S101"},
{0x00, 0x02, "LM3S102"},
{0x01, 0xBF, "LM3S1110"},
{0x01, 0xC3, "LM3S1133"},
{0x01, 0xC5, "LM3S1138"},
{0x01, 0xC1, "LM3S1150"},
{0x01, 0xC4, "LM3S1162"},
{0x01, 0xC2, "LM3S1165"},
{0x01, 0xEC, "LM3S1166"},
{0x01, 0xC6, "LM3S1332"},
{0x01, 0xBC, "LM3S1435"},
{0x01, 0xBA, "LM3S1439"},
{0x01, 0xBB, "LM3S1512"},
{0x01, 0xC7, "LM3S1538"},
{0x01, 0xDB, "LM3S1601"},
{0x03, 0x06, "LM3S1607"},
{0x01, 0xDA, "LM3S1608"},
{0x01, 0xC0, "LM3S1620"},
{0x04, 0xCD, "LM3S1621"},
{0x03, 0x03, "LM3S1625"},
{0x03, 0x04, "LM3S1626"},
{0x03, 0x05, "LM3S1627"},
{0x01, 0xB3, "LM3S1635"},
{0x01, 0xEB, "LM3S1636"},
{0x01, 0xBD, "LM3S1637"},
{0x04, 0xB1, "LM3S1651"},
{0x01, 0xB9, "LM3S1751"},
{0x03, 0x10, "LM3S1776"},
{0x04, 0x16, "LM3S1811"},
{0x04, 0x3D, "LM3S1816"},
{0x01, 0xB4, "LM3S1850"},
{0x01, 0xDD, "LM3S1911"},
{0x01, 0xDC, "LM3S1918"},
{0x01, 0xB7, "LM3S1937"},
{0x01, 0xBE, "LM3S1958"},
{0x01, 0xB5, "LM3S1960"},
{0x01, 0xB8, "LM3S1968"},
{0x01, 0xEA, "LM3S1969"},
{0x04, 0xCE, "LM3S1B21"},
{0x06, 0xCA, "LM3S1C21"},
{0x06, 0xCB, "LM3S1C26"},
{0x06, 0x98, "LM3S1C58"},
{0x06, 0xB0, "LM3S1D21"},
{0x06, 0xCC, "LM3S1D26"},
{0x06, 0x1D, "LM3S1F11"},
{0x06, 0x1B, "LM3S1F16"},
{0x06, 0xAF, "LM3S1G21"},
{0x06, 0x95, "LM3S1G58"},
{0x06, 0x1E, "LM3S1H11"},
{0x06, 0x1C, "LM3S1H16"},
{0x04, 0x0F, "LM3S1J11"},
{0x04, 0x3C, "LM3S1J16"},
{0x04, 0x0E, "LM3S1N11"},
{0x04, 0x3B, "LM3S1N16"},
{0x04, 0xB2, "LM3S1P51"},
{0x04, 0x9E, "LM3S1R21"},
{0x04, 0xC9, "LM3S1R26"},
{0x04, 0x30, "LM3S1W16"},
{0x04, 0x2F, "LM3S1Z16"},
{0x01, 0x51, "LM3S2110"},
{0x01, 0x84, "LM3S2139"},
{0x03, 0x39, "LM3S2276"},
{0x01, 0xA2, "LM3S2410"},
{0x01, 0x59, "LM3S2412"},
{0x01, 0x56, "LM3S2432"},
{0x01, 0x5A, "LM3S2533"},
{0x01, 0xE1, "LM3S2601"},
{0x01, 0xE0, "LM3S2608"},
{0x03, 0x33, "LM3S2616"},
{0x01, 0x57, "LM3S2620"},
{0x01, 0x85, "LM3S2637"},
{0x01, 0x53, "LM3S2651"},
{0x03, 0x80, "LM3S2671"},
{0x03, 0x50, "LM3S2678"},
{0x01, 0xA4, "LM3S2730"},
{0x01, 0x52, "LM3S2739"},
{0x03, 0x3A, "LM3S2776"},
{0x04, 0x6D, "LM3S2793"},
{0x01, 0xE3, "LM3S2911"},
{0x01, 0xE2, "LM3S2918"},
{0x01, 0xED, "LM3S2919"},
{0x01, 0x54, "LM3S2939"},
{0x01, 0x8F, "LM3S2948"},
{0x01, 0x58, "LM3S2950"},
{0x01, 0x55, "LM3S2965"},
{0x04, 0x6C, "LM3S2B93"},
{0x06, 0x94, "LM3S2D93"},
{0x06, 0x93, "LM3S2U93"},
{0x00, 0x19, "LM3S300"},
{0x00, 0x11, "LM3S301"},
{0x00, 0x1A, "LM3S308"},
{0x00, 0x12, "LM3S310"},
{0x00, 0x13, "LM3S315"},
{0x00, 0x14, "LM3S316"},
{0x00, 0x17, "LM3S317"},
{0x00, 0x15, "LM3S328"},
{0x03, 0x08, "LM3S3634"},
{0x03, 0x43, "LM3S3651"},
{0x04, 0xC8, "LM3S3654"},
{0x03, 0x44, "LM3S3739"},
{0x03, 0x49, "LM3S3748"},
{0x03, 0x45, "LM3S3749"},
{0x04, 0x42, "LM3S3826"},
{0x04, 0x41, "LM3S3J26"},
{0x04, 0x40, "LM3S3N26"},
{0x04, 0x3F, "LM3S3W26"},
{0x04, 0x3E, "LM3S3Z26"},
{0x03, 0x81, "LM3S5632"},
{0x04, 0x0C, "LM3S5651"},
{0x03, 0x8A, "LM3S5652"},
{0x04, 0x4D, "LM3S5656"},
{0x03, 0x91, "LM3S5662"},
{0x03, 0x96, "LM3S5732"},
{0x03, 0x97, "LM3S5737"},
{0x03, 0xA0, "LM3S5739"},
{0x03, 0x99, "LM3S5747"},
{0x03, 0xA7, "LM3S5749"},
{0x03, 0x9A, "LM3S5752"},
{0x03, 0x9C, "LM3S5762"},
{0x04, 0x69, "LM3S5791"},
{0x04, 0x0B, "LM3S5951"},
{0x04, 0x4E, "LM3S5956"},
{0x04, 0x68, "LM3S5B91"},
{0x06, 0x2E, "LM3S5C31"},
{0x06, 0x2C, "LM3S5C36"},
{0x06, 0x5E, "LM3S5C51"},
{0x06, 0x5B, "LM3S5C56"},
{0x06, 0x5F, "LM3S5D51"},
{0x06, 0x5C, "LM3S5D56"},
{0x06, 0x87, "LM3S5D91"},
{0x06, 0x2D, "LM3S5G31"},
{0x06, 0x1F, "LM3S5G36"},
{0x06, 0x5D, "LM3S5G51"},
{0x06, 0x4F, "LM3S5G56"},
{0x04, 0x09, "LM3S5K31"},
{0x04, 0x4A, "LM3S5K36"},
{0x04, 0x0A, "LM3S5P31"},
{0x04, 0x48, "LM3S5P36"},
{0x04, 0xB6, "LM3S5P3B"},
{0x04, 0x0D, "LM3S5P51"},
{0x04, 0x4C, "LM3S5P56"},
{0x04, 0x07, "LM3S5R31"},
{0x04, 0x4B, "LM3S5R36"},
{0x04, 0x47, "LM3S5T36"},
{0x06, 0x7F, "LM3S5U91"},
{0x04, 0x46, "LM3S5Y36"},
{0x00, 0x2A, "LM3S600"},
{0x00, 0x21, "LM3S601"},
{0x00, 0x2B, "LM3S608"},
{0x00, 0x22, "LM3S610"},
{0x01, 0xA1, "LM3S6100"},
{0x00, 0x23, "LM3S611"},
{0x01, 0x74, "LM3S6110"},
{0x00, 0x24, "LM3S612"},
{0x00, 0x25, "LM3S613"},
{0x00, 0x26, "LM3S615"},
{0x00, 0x28, "LM3S617"},
{0x00, 0x29, "LM3S618"},
{0x00, 0x27, "LM3S628"},
{0x01, 0xA5, "LM3S6420"},
{0x01, 0x82, "LM3S6422"},
{0x01, 0x75, "LM3S6432"},
{0x01, 0x76, "LM3S6537"},
{0x01, 0x71, "LM3S6610"},
{0x01, 0xE7, "LM3S6611"},
{0x01, 0xE6, "LM3S6618"},
{0x01, 0x83, "LM3S6633"},
{0x01, 0x8B, "LM3S6637"},
{0x01, 0xA3, "LM3S6730"},
{0x01, 0x77, "LM3S6753"},
{0x01, 0xE9, "LM3S6911"},
{0x01, 0xE8, "LM3S6918"},
{0x01, 0x89, "LM3S6938"},
{0x01, 0x72, "LM3S6950"},
{0x01, 0x78, "LM3S6952"},
{0x01, 0x73, "LM3S6965"},
{0x06, 0xAA, "LM3S6C11"},
{0x06, 0xAC, "LM3S6C65"},
{0x06, 0x9F, "LM3S6G11"},
{0x06, 0xAB, "LM3S6G65"},
{0x00, 0x38, "LM3S800"},
{0x00, 0x31, "LM3S801"},
{0x00, 0x39, "LM3S808"},
{0x00, 0x32, "LM3S811"},
{0x00, 0x33, "LM3S812"},
{0x00, 0x34, "LM3S815"},
{0x00, 0x36, "LM3S817"},
{0x00, 0x37, "LM3S818"},
{0x00, 0x35, "LM3S828"},
{0x01, 0x64, "LM3S8530"},
{0x01, 0x8E, "LM3S8538"},
{0x01, 0x61, "LM3S8630"},
{0x01, 0x63, "LM3S8730"},
{0x01, 0x8D, "LM3S8733"},
{0x01, 0x86, "LM3S8738"},
{0x01, 0x65, "LM3S8930"},
{0x01, 0x8C, "LM3S8933"},
{0x01, 0x88, "LM3S8938"},
{0x01, 0xA6, "LM3S8962"},
{0x01, 0x62, "LM3S8970"},
{0x01, 0xD7, "LM3S8971"},
{0x06, 0xAE, "LM3S8C62"},
{0x06, 0xAD, "LM3S8G62"},
{0x04, 0xCF, "LM3S9781"},
{0x04, 0x67, "LM3S9790"},
{0x04, 0x6B, "LM3S9792"},
{0x04, 0x2D, "LM3S9971"},
{0x04, 0x20, "LM3S9997"},
{0x04, 0xD0, "LM3S9B81"},
{0x04, 0x66, "LM3S9B90"},
{0x04, 0x6A, "LM3S9B92"},
{0x04, 0x6E, "LM3S9B95"},
{0x04, 0x6F, "LM3S9B96"},
{0x04, 0x1D, "LM3S9BN2"},
{0x04, 0x1E, "LM3S9BN5"},
{0x04, 0x1F, "LM3S9BN6"},
{0x06, 0x70, "LM3S9C97"},
{0x06, 0xA9, "LM3S9D81"},
{0x06, 0x7E, "LM3S9D90"},
{0x06, 0x92, "LM3S9D92"},
{0x06, 0x9D, "LM3S9D96"},
{0x06, 0x7B, "LM3S9DN5"},
{0x06, 0x7C, "LM3S9DN6"},
{0x06, 0x60, "LM3S9G97"},
{0x06, 0x79, "LM3S9GN5"},
{0x04, 0x1B, "LM3S9L71"},
{0x04, 0x18, "LM3S9L97"},
{0x06, 0xA8, "LM3S9U81"},
{0x06, 0x7D, "LM3S9U90"},
{0x06, 0x90, "LM3S9U92"},
{0x06, 0x9B, "LM3S9U96"},
{0x05, 0x01, "LM4F120B2QR/TM4C1233C3PM"},
{0x05, 0x02, "LM4F120C4QR/TM4C1233D5PM"},
{0x05, 0x03, "LM4F120E5QR/TM4C1233E6PM"},
{0x05, 0x04, "LM4F120H5QR/TM4C1233H6PM"},
{0x05, 0x08, "LM4F121B2QR/TM4C1232C3PM"},
{0x05, 0x09, "LM4F121C4QR/TM4C1232D5PM"},
{0x05, 0x0A, "LM4F121E5QR/TM4C1232E6PM"},
{0x05, 0x0B, "LM4F121H5QR/TM4C1232H6PM"},
{0x05, 0x10, "LM4F110E5QR/TM4C1231E6PM"},
{0x05, 0x11, "LM4F110H5QR/TM4C1231H6PM"},
{0x05, 0x18, "LM4F110B2QR/TM4C1231C3PM"},
{0x05, 0x19, "LM4F110C4QR/TM4C1231D5PM"},
{0x05, 0x20, "LM4F111E5QR/TM4C1230E6PM"},
{0x05, 0x21, "LM4F111H5QR/TM4C1230H6PM"},
{0x05, 0x22, "LM4F111B2QR/TM4C1230C3PM"},
{0x05, 0x23, "LM4F111C4QR/TM4C1230D5PM"},
{0x05, 0x30, "LM4F112E5QC/TM4C1231E6PZ"},
{0x05, 0x31, "LM4F112H5QC/TM4C1231H6PZ"},
{0x05, 0x35, "LM4F112H5QD/TM4C1231H6PGE"},
{0x05, 0x36, "LM4F112C4QC/TM4C1231D5PZ"},
{0x05, 0x40, "LM4F130E5QR/TM4C1237E6PM"},
{0x05, 0x41, "LM4F130H5QR/TM4C1237H6PM"},
{0x05, 0x48, "LM4F130C4QR/TM4C1237D5PM"},
{0x05, 0x50, "LM4F131E5QR/TM4C1236E6PM"},
{0x05, 0x51, "LM4F131H5QR/TM4C1236H6PM"},
{0x05, 0x52, "LM4F131C4QR/TM4C1236D5PM"},
{0x05, 0x60, "LM4F132E5QC/TM4C1237E6PZ"},
{0x05, 0x61, "LM4F132H5QC/TM4C1237H6PZ"},
{0x05, 0x65, "LM4F132H5QD/TM4C1237H6PGE"},
{0x05, 0x66, "LM4F132C4QC/TM4C1237D5PZ"},
{0x05, 0x70, "LM4F210E5QR/TM4C123BE6PM"},
{0x05, 0x73, "LM4F210H5QR/TM4C123BH6PM"},
{0x05, 0x80, "LM4F211E5QR/TM4C123AE6PM"},
{0x05, 0x83, "LM4F211H5QR/TM4C123AH6PM"},
{0x05, 0xA0, "LM4F230E5QR/TM4C123GE6PM"},
{0x05, 0xA1, "LM4F230H5QR/TM4C123GH6PM"},
{0x05, 0xB0, "LM4F231E5QR/TM4C123FE6PM"},
{0x05, 0xB1, "LM4F231H5QR/TM4C123FH6PM"},
{0x05, 0xC0, "LM4F232E5QC/TM4C123GE6PZ"},
{0x05, 0xC1, "LM4F232H5QC/TM4C123GH6PZ"},
{0x05, 0xC3, "LM4F212E5QC/TM4C123BE6PZ"},
{0x05, 0xC4, "LM4F212H5QC/TM4C123BH6PZ"},
{0x05, 0xC5, "LM4F232H5QD/TM4C123GH6PGE"},
{0x05, 0xC6, "LM4F212H5QD/TM4C123BH6PGE"},
{0x05, 0xD0, "LM4F122C4QC/TM4C1233D5PZ"},
{0x05, 0xD1, "LM4F122E5QC/TM4C1233E6PZ"},
{0x05, 0xD2, "LM4F122H5QC/TM4C1233H6PZ"},
{0x05, 0xD6, "LM4F122H5QD/TM4C1233H6PGE"},
{0x05, 0xE1, "LM4FSXLH5BB"},
{0x05, 0xE3, "LM4F232H5BB/TM4C123GH6ZRB"},
{0x05, 0xE4, "LM4FS99H5BB"},
{0x05, 0xE5, "LM4FS1AH5BB"},
{0x05, 0xE9, "LM4F212H5BB/TM4C123BH6ZRB"},
{0x05, 0xEA, "LM4FS1GH5BB"},
{0x05, 0xF0, "TM4C123GH6ZXR"},
{0x0A, 0x19, "TM4C1290NCPDT"},
{0x0A, 0x1B, "TM4C1290NCZAD"},
{0x0A, 0x1C, "TM4C1292NCPDT"},
{0x0A, 0x1E, "TM4C1292NCZAD"},
{0x0A, 0x1F, "TM4C1294NCPDT"},
{0x0A, 0x21, "TM4C1294NCZAD"},
{0x0A, 0x22, "TM4C1297NCZAD"},
{0x0A, 0x23, "TM4C1299NCZAD"},
{0x0A, 0x24, "TM4C129CNCPDT"},
{0x0A, 0x26, "TM4C129CNCZAD"},
{0x0A, 0x27, "TM4C129DNCPDT"},
{0x0A, 0x29, "TM4C129DNCZAD"},
{0x0A, 0x2D, "TM4C129ENCPDT"},
{0x0A, 0x2F, "TM4C129ENCZAD"},
{0x0A, 0x30, "TM4C129LNCZAD"},
{0x0A, 0x32, "TM4C129XNCZAD"},
{0x0A, 0x34, "TM4C1294KCPDT"},
{0x0A, 0x35, "TM4C129EKCPDT"},
{0x0A, 0x36, "TM4C1299KCZAD"},
{0x0A, 0x37, "TM4C129XKCZAD"},
{0xFF, 0x00, "Unknown Part"}
};
static const char * const StellarisClassname[] = {
"Sandstorm",
"Fury",
"Unknown",
"DustDevil",
"Tempest",
"Blizzard/TM4C123x",
"Firestorm",
"",
"",
"",
"Snowflake",
};
/***************************************************************************
* openocd command interface *
***************************************************************************/
/* flash_bank stellaris <base> <size> 0 0 <target#>
*/
FLASH_BANK_COMMAND_HANDLER(stellaris_flash_bank_command)
{
struct stellaris_flash_bank *stellaris_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
stellaris_info = calloc(sizeof(struct stellaris_flash_bank), 1);
bank->base = 0x0;
bank->driver_priv = stellaris_info;
stellaris_info->target_name = "Unknown target";
/* part wasn't probed for info yet */
stellaris_info->did1 = 0;
/* TODO Specify the main crystal speed in kHz using an optional
* argument; ditto, the speed of an external oscillator used
* instead of a crystal. Avoid programming flash using IOSC.
*/
return ERROR_OK;
}
static int get_stellaris_info(struct flash_bank *bank, char *buf, int buf_size)
{
int printed;
struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
if (stellaris_info->did1 == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
/* Read main and master clock frequency register */
stellaris_read_clock_info(bank);
printed = snprintf(buf,
buf_size,
"\nTI/LMI Stellaris information: Chip is "
"class %i (%s) %s rev %c%i\n",
stellaris_info->target_class,
StellarisClassname[stellaris_info->target_class],
stellaris_info->target_name,
(int)('A' + ((stellaris_info->did0 >> 8) & 0xFF)),
(int)((stellaris_info->did0) & 0xFF));
buf += printed;
buf_size -= printed;
printed = snprintf(buf,
buf_size,
"did1: 0x%8.8" PRIx32 ", arch: 0x%4.4" PRIx32
", eproc: %s, ramsize: %" PRIu32 "k, flashsize: %" PRIu32 "k\n",
stellaris_info->did1,
stellaris_info->did1,
"ARMv7M",
stellaris_info->sramsiz,
(uint32_t)(stellaris_info->num_pages * stellaris_info->pagesize / 1024));
buf += printed;
buf_size -= printed;
snprintf(buf,
buf_size,
"master clock: %ikHz%s, "
"rcc is 0x%" PRIx32 ", rcc2 is 0x%" PRIx32 ", "
"pagesize: %" PRIu32 ", pages: %" PRIu32,
(int)(stellaris_info->mck_freq / 1000),
stellaris_info->mck_desc,
stellaris_info->rcc,
stellaris_info->rcc2,
stellaris_info->pagesize,
stellaris_info->num_pages);
return ERROR_OK;
}
/***************************************************************************
* chip identification and status *
***************************************************************************/
/* Set the flash timing register to match current clocking */
static void stellaris_set_flash_timing(struct flash_bank *bank)
{
struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
struct target *target = bank->target;
uint32_t usecrl = (stellaris_info->mck_freq/1000000ul-1);
/* only valid for Sandstorm and Fury class devices */
if (stellaris_info->target_class > 1)
return;
LOG_DEBUG("usecrl = %i", (int)(usecrl));
target_write_u32(target, SCB_BASE | USECRL, usecrl);
}
static const unsigned rcc_xtal[32] = {
[0x00] = 1000000, /* no pll */
[0x01] = 1843200, /* no pll */
[0x02] = 2000000, /* no pll */
[0x03] = 2457600, /* no pll */
[0x04] = 3579545,
[0x05] = 3686400,
[0x06] = 4000000, /* usb */
[0x07] = 4096000,
[0x08] = 4915200,
[0x09] = 5000000, /* usb */
[0x0a] = 5120000,
[0x0b] = 6000000, /* (reset) usb */
[0x0c] = 6144000,
[0x0d] = 7372800,
[0x0e] = 8000000, /* usb */
[0x0f] = 8192000,
/* parts before DustDevil use just 4 bits for xtal spec */
[0x10] = 10000000, /* usb */
[0x11] = 12000000, /* usb */
[0x12] = 12288000,
[0x13] = 13560000,
[0x14] = 14318180,
[0x15] = 16000000, /* usb */
[0x16] = 16384000,
};
/** Read clock configuration and set stellaris_info->usec_clocks. */
static void stellaris_read_clock_info(struct flash_bank *bank)
{
struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
struct target *target = bank->target;
uint32_t rcc, rcc2, pllcfg, sysdiv, usesysdiv, bypass, oscsrc;
unsigned xtal;
unsigned long mainfreq;
target_read_u32(target, SCB_BASE | RCC, &rcc);
LOG_DEBUG("Stellaris RCC %" PRIx32 "", rcc);
target_read_u32(target, SCB_BASE | RCC2, &rcc2);
LOG_DEBUG("Stellaris RCC2 %" PRIx32 "", rcc);
target_read_u32(target, SCB_BASE | PLLCFG, &pllcfg);
LOG_DEBUG("Stellaris PLLCFG %" PRIx32 "", pllcfg);
stellaris_info->rcc = rcc;
stellaris_info->rcc2 = rcc2;
sysdiv = (rcc >> 23) & 0xF;
usesysdiv = (rcc >> 22) & 0x1;
bypass = (rcc >> 11) & 0x1;
oscsrc = (rcc >> 4) & 0x3;
xtal = (rcc >> 6) & stellaris_info->xtal_mask;
/* NOTE: post-Sandstorm parts have RCC2 which may override
* parts of RCC ... with more sysdiv options, option for
* 32768 Hz mainfreq, PLL controls. On Sandstorm it reads
* as zero, so the "use RCC2" flag is always clear.
*/
if (rcc2 & (1 << 31)) {
sysdiv = (rcc2 >> 23) & 0x3F;
bypass = (rcc2 >> 11) & 0x1;
oscsrc = (rcc2 >> 4) & 0x7;
/* FIXME Tempest parts have an additional lsb for
* fractional sysdiv (200 MHz / 2.5 == 80 MHz)
*/
}
stellaris_info->mck_desc = "";
switch (oscsrc) {
case 0: /* MOSC */
mainfreq = rcc_xtal[xtal];
break;
case 1: /* IOSC */
mainfreq = stellaris_info->iosc_freq;
stellaris_info->mck_desc = stellaris_info->iosc_desc;
break;
case 2: /* IOSC/4 */
mainfreq = stellaris_info->iosc_freq / 4;
stellaris_info->mck_desc = stellaris_info->iosc_desc;
break;
case 3: /* lowspeed */
/* Sandstorm doesn't have this 30K +/- 30% osc */
mainfreq = 30000;
stellaris_info->mck_desc = " (±30%)";
break;
case 8: /* hibernation osc */
/* not all parts support hibernation */
mainfreq = 32768;
break;
default: /* NOTREACHED */
mainfreq = 0;
break;
}
/* PLL is used if it's not bypassed; its output is 200 MHz
* even when it runs at 400 MHz (adds divide-by-two stage).
*/
if (!bypass)
mainfreq = 200000000;
if (usesysdiv)
stellaris_info->mck_freq = mainfreq/(1 + sysdiv);
else
stellaris_info->mck_freq = mainfreq;
}
/* Read device id register, main clock frequency register and fill in driver info structure */
static int stellaris_read_part_info(struct flash_bank *bank)
{
struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
struct target *target = bank->target;
uint32_t did0, did1, ver, fam;
int i;
/* Read and parse chip identification register */
target_read_u32(target, SCB_BASE | DID0, &did0);
target_read_u32(target, SCB_BASE | DID1, &did1);
target_read_u32(target, SCB_BASE | DC0, &stellaris_info->dc0);
target_read_u32(target, SCB_BASE | DC1, &stellaris_info->dc1);
LOG_DEBUG("did0 0x%" PRIx32 ", did1 0x%" PRIx32 ", dc0 0x%" PRIx32 ", dc1 0x%" PRIx32 "",
did0, did1, stellaris_info->dc0, stellaris_info->dc1);
ver = DID0_VER(did0);
if ((ver != 0) && (ver != 1)) {
LOG_WARNING("Unknown did0 version, cannot identify target");
return ERROR_FLASH_OPERATION_FAILED;
}
if (did1 == 0) {
LOG_WARNING("Cannot identify target as a Stellaris");
return ERROR_FLASH_OPERATION_FAILED;
}
ver = did1 >> 28;
fam = (did1 >> 24) & 0xF;
if (((ver != 0) && (ver != 1)) || (fam != 0)) {
LOG_WARNING("Unknown did1 version/family.");
return ERROR_FLASH_OPERATION_FAILED;
}
/* For Sandstorm, Fury, DustDevil: current data sheets say IOSC
* is 12 MHz, but some older parts have 15 MHz. A few data sheets
* even give _both_ numbers! We'll use current numbers; IOSC is
* always approximate.
*
* For Tempest: IOSC is calibrated, 16 MHz
* For Blizzard: IOSC is calibrated, 16 MHz
* For Firestorm: IOSC is calibrated, 16 MHz
*/
stellaris_info->iosc_freq = 12000000;
stellaris_info->iosc_desc = " (±30%)";
stellaris_info->xtal_mask = 0x0f;
/* get device class */
if (DID0_VER(did0) > 0) {
stellaris_info->target_class = (did0 >> 16) & 0xFF;
} else {
/* Sandstorm class */
stellaris_info->target_class = 0;
}
switch (stellaris_info->target_class) {
case 0: /* Sandstorm */
/*
* Current (2009-August) parts seem to be rev C2 and use 12 MHz.
* Parts before rev C0 used 15 MHz; some C0 parts use 15 MHz
* (LM3S618), but some other C0 parts are 12 MHz (LM3S811).
*/
if (((did0 >> 8) & 0xff) < 2) {
stellaris_info->iosc_freq = 15000000;
stellaris_info->iosc_desc = " (±50%)";
}
break;
case 1: /* Fury */
break;
case 4: /* Tempest */
case 5: /* Blizzard */
case 6: /* Firestorm */
case 0xa: /* Snowflake */
stellaris_info->iosc_freq = 16000000; /* +/- 1% */
stellaris_info->iosc_desc = " (±1%)";
/* FALL THROUGH */
case 3: /* DustDevil */
stellaris_info->xtal_mask = 0x1f;
break;
default:
LOG_WARNING("Unknown did0 class");
}
for (i = 0; StellarisParts[i].partno; i++) {
if ((StellarisParts[i].partno == ((did1 >> 16) & 0xFF)) &&
(StellarisParts[i].class == stellaris_info->target_class))
break;
}
stellaris_info->target_name = StellarisParts[i].partname;
stellaris_info->did0 = did0;
stellaris_info->did1 = did1;
if (stellaris_info->target_class == 5) { /* Blizzard */
target_read_u32(target, FLASH_FSIZE, &stellaris_info->fsize);
target_read_u32(target, FLASH_SSIZE, &stellaris_info->ssize);
stellaris_info->num_pages = 2 * (1 + (stellaris_info->fsize & 0xFFFF));
stellaris_info->sramsiz = (1 + (stellaris_info->ssize & 0xFFFF)) / 4;
stellaris_info->pagesize = 1024;
} else if (stellaris_info->target_class == 0xa) { /* Snowflake */
target_read_u32(target, FLASH_FSIZE, &stellaris_info->fsize);
target_read_u32(target, FLASH_SSIZE, &stellaris_info->ssize);
stellaris_info->pagesize = (1 << ((stellaris_info->fsize >> 16) & 7)) * 1024;
stellaris_info->num_pages = 2048 * (1 + (stellaris_info->fsize & 0xFFFF)) /
stellaris_info->pagesize;
stellaris_info->sramsiz = (1 + (stellaris_info->ssize & 0xFFFF)) / 4;
} else {
stellaris_info->num_pages = 2 * (1 + (stellaris_info->dc0 & 0xFFFF));
stellaris_info->sramsiz = (1 + ((stellaris_info->dc0 >> 16) & 0xFFFF)) / 4;
stellaris_info->pagesize = 1024;
}
/* REVISIT for at least Tempest parts, read NVMSTAT.FWB too.
* That exposes a 32-word Flash Write Buffer ... enabling
* writes of more than one word at a time.
*/
return ERROR_OK;
}
/***************************************************************************
* flash operations *
***************************************************************************/
static int stellaris_protect_check(struct flash_bank *bank)
{
struct stellaris_flash_bank *stellaris = bank->driver_priv;
struct target *target = bank->target;
uint32_t flash_sizek = stellaris->pagesize / 1024 *
stellaris->num_pages;
uint32_t fmppe_addr;
int status = ERROR_OK;
if (stellaris->did1 == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
for (unsigned int i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_protected = -1;
/* Read each Flash Memory Protection Program Enable (FMPPE) register
* to report any pages that we can't write. Ignore the Read Enable
* register (FMPRE).
*/
if (stellaris->target_class >= 0x0a || flash_sizek > 64)
fmppe_addr = SCB_BASE | FMPPE0;
else
fmppe_addr = SCB_BASE | FMPPE;
unsigned int page = 0, lockbitnum, lockbitcnt = flash_sizek / 2;
unsigned int bits_per_page = stellaris->pagesize / 2048;
/* Every lock bit always corresponds to a 2k region */
for (lockbitnum = 0; lockbitnum < lockbitcnt; lockbitnum += 32) {
uint32_t fmppe;
target_read_u32(target, fmppe_addr, &fmppe);
for (unsigned int i = 0; i < 32 && lockbitnum + i < lockbitcnt; i++) {
bool protect = !(fmppe & (1 << i));
if (bits_per_page) {
bank->sectors[page++].is_protected = protect;
i += bits_per_page - 1;
} else { /* 1024k pages, every lockbit covers 2 pages */
bank->sectors[page++].is_protected = protect;
bank->sectors[page++].is_protected = protect;
}
}
fmppe_addr += 4;
}
return status;
}
static int stellaris_erase(struct flash_bank *bank, unsigned int first,
unsigned int last)
{
uint32_t flash_fmc, flash_cris;
struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
struct target *target = bank->target;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (stellaris_info->did1 == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
if ((last < first) || (last >= stellaris_info->num_pages))
return ERROR_FLASH_SECTOR_INVALID;
if ((first == 0) && (last == (stellaris_info->num_pages - 1)))
return stellaris_mass_erase(bank);
/* Refresh flash controller timing */
stellaris_read_clock_info(bank);
stellaris_set_flash_timing(bank);
/* Clear and disable flash programming interrupts */
target_write_u32(target, FLASH_CIM, 0);
target_write_u32(target, FLASH_MISC, PMISC | AMISC);
/* REVISIT this clobbers state set by any halted firmware ...
* it might want to process those IRQs.
*/
for (unsigned int banknr = first; banknr <= last; banknr++) {
/* Address is first word in page */
target_write_u32(target, FLASH_FMA, banknr * stellaris_info->pagesize);
/* Write erase command */
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_ERASE);
/* Wait until erase complete */
do {
target_read_u32(target, FLASH_FMC, &flash_fmc);
} while (flash_fmc & FMC_ERASE);
/* Check access violations */
target_read_u32(target, FLASH_CRIS, &flash_cris);
if (flash_cris & (AMASK)) {
LOG_WARNING("Error erasing flash page %i, flash_cris 0x%" PRIx32 "",
banknr, flash_cris);
target_write_u32(target, FLASH_CRIS, 0);
return ERROR_FLASH_OPERATION_FAILED;
}
bank->sectors[banknr].is_erased = 1;
}
return ERROR_OK;
}
static int stellaris_protect(struct flash_bank *bank, int set,
unsigned int first, unsigned int last)
{
struct stellaris_flash_bank *stellaris = bank->driver_priv;
struct target *target = bank->target;
uint32_t flash_fmc, flash_cris;
unsigned int bits_per_page = stellaris->pagesize / 2048;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (!set) {
LOG_ERROR("Hardware doesn't support page-level unprotect. "
"Try the 'recover' command.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (stellaris->did1 == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
if (stellaris->target_class == 0x03 &&
!((stellaris->did0 >> 8) & 0xFF) &&
!((stellaris->did0) & 0xFF)) {
LOG_ERROR("DustDevil A0 parts can't be unprotected, see errata; refusing to proceed");
return ERROR_FLASH_OPERATION_FAILED;
}
if (!bits_per_page && (first % 2 || !(last % 2))) {
LOG_ERROR("Can't protect unaligned pages");
return ERROR_FLASH_SECTOR_INVALID;
}
/* Refresh flash controller timing */
stellaris_read_clock_info(bank);
stellaris_set_flash_timing(bank);
/* Clear and disable flash programming interrupts */
target_write_u32(target, FLASH_CIM, 0);
target_write_u32(target, FLASH_MISC, PMISC | AMISC);
uint32_t flash_sizek = stellaris->pagesize / 1024 *
stellaris->num_pages;
uint32_t fmppe_addr;
if (stellaris->target_class >= 0x0a || flash_sizek > 64)
fmppe_addr = SCB_BASE | FMPPE0;
else
fmppe_addr = SCB_BASE | FMPPE;
unsigned int page = 0;
unsigned int lockbitnum, lockbitcnt = flash_sizek / 2;
/* Every lock bit always corresponds to a 2k region */
for (lockbitnum = 0; lockbitnum < lockbitcnt; lockbitnum += 32) {
uint32_t fmppe;
target_read_u32(target, fmppe_addr, &fmppe);
for (unsigned int i = 0;
i < 32 && lockbitnum + i < lockbitcnt;
i++) {
if (page >= first && page <= last)
fmppe &= ~(1 << i);
if (bits_per_page) {
if (!((i + 1) % bits_per_page))
page++;
} else { /* 1024k pages, every lockbit covers 2 pages */
page += 2;
}
}
target_write_u32(target, fmppe_addr, fmppe);
/* Commit FMPPE* */
target_write_u32(target, FLASH_FMA, 1 + lockbitnum / 16);
/* Write commit command */
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT);
/* Wait until commit complete */
do {
target_read_u32(target, FLASH_FMC, &flash_fmc);
} while (flash_fmc & FMC_COMT);
/* Check access violations */
target_read_u32(target, FLASH_CRIS, &flash_cris);
if (flash_cris & (AMASK)) {
LOG_WARNING("Error setting flash page protection, flash_cris 0x%" PRIx32 "", flash_cris);
target_write_u32(target, FLASH_CRIS, 0);
return ERROR_FLASH_OPERATION_FAILED;
}
fmppe_addr += 4;
}
return ERROR_OK;
}
/* see contrib/loaders/flash/stellaris.s for src */
static const uint8_t stellaris_write_code[] = {
/* write: */
0xDF, 0xF8, 0x40, 0x40, /* ldr r4, pFLASH_CTRL_BASE */
0xDF, 0xF8, 0x40, 0x50, /* ldr r5, FLASHWRITECMD */
/* wait_fifo: */
0xD0, 0xF8, 0x00, 0x80, /* ldr r8, [r0, #0] */
0xB8, 0xF1, 0x00, 0x0F, /* cmp r8, #0 */
0x17, 0xD0, /* beq exit */
0x47, 0x68, /* ldr r7, [r0, #4] */
0x47, 0x45, /* cmp r7, r8 */
0xF7, 0xD0, /* beq wait_fifo */
/* mainloop: */
0x22, 0x60, /* str r2, [r4, #0] */
0x02, 0xF1, 0x04, 0x02, /* add r2, r2, #4 */
0x57, 0xF8, 0x04, 0x8B, /* ldr r8, [r7], #4 */
0xC4, 0xF8, 0x04, 0x80, /* str r8, [r4, #4] */
0xA5, 0x60, /* str r5, [r4, #8] */
/* busy: */
0xD4, 0xF8, 0x08, 0x80, /* ldr r8, [r4, #8] */
0x18, 0xF0, 0x01, 0x0F, /* tst r8, #1 */
0xFA, 0xD1, /* bne busy */
0x8F, 0x42, /* cmp r7, r1 */
0x28, 0xBF, /* it cs */
0x00, 0xF1, 0x08, 0x07, /* addcs r7, r0, #8 */
0x47, 0x60, /* str r7, [r0, #4] */
0x01, 0x3B, /* subs r3, r3, #1 */
0x03, 0xB1, /* cbz r3, exit */
0xE2, 0xE7, /* b wait_fifo */
/* exit: */
0x00, 0xBE, /* bkpt #0 */
/* pFLASH_CTRL_BASE: */
0x00, 0xD0, 0x0F, 0x40, /* .word 0x400FD000 */
/* FLASHWRITECMD: */
0x01, 0x00, 0x42, 0xA4 /* .word 0xA4420001 */
};
static int stellaris_write_block(struct flash_bank *bank,
const uint8_t *buffer, uint32_t offset, uint32_t wcount)
{
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[4];
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(stellaris_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(stellaris_write_code),
stellaris_write_code);
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
init_reg_param(&reg_params[3], "r3", 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);
retval = target_run_flash_async_algorithm(target, buffer, wcount, 4,
0, NULL,
4, reg_params,
source->address, source->size,
write_algorithm->address, 0,
&armv7m_info);
if (retval == ERROR_FLASH_OPERATION_FAILED)
LOG_ERROR("error %d executing stellaris flash write algorithm", retval);
target_free_working_area(target, write_algorithm);
target_free_working_area(target, source);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
return retval;
}
static int stellaris_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
struct target *target = bank->target;
uint32_t address = offset;
uint32_t flash_cris, flash_fmc;
uint32_t words_remaining = (count / 4);
uint32_t bytes_remaining = (count & 0x00000003);
uint32_t bytes_written = 0;
int retval;
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 (stellaris_info->did1 == 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;
/* Refresh flash controller timing */
stellaris_read_clock_info(bank);
stellaris_set_flash_timing(bank);
/* Clear and disable flash programming interrupts */
target_write_u32(target, FLASH_CIM, 0);
target_write_u32(target, FLASH_MISC, PMISC | AMISC);
/* REVISIT this clobbers state set by any halted firmware ...
* it might want to process those IRQs.
*/
/* multiple words to be programmed? */
if (words_remaining > 0) {
/* try using a block write */
retval = stellaris_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 if (retval == ERROR_FLASH_OPERATION_FAILED) {
/* if an error occurred, we examine the reason, and quit */
target_read_u32(target, FLASH_CRIS, &flash_cris);
LOG_ERROR("flash writing failed with CRIS: 0x%" PRIx32 "", flash_cris);
return ERROR_FLASH_OPERATION_FAILED;
}
} else {
buffer += words_remaining * 4;
address += words_remaining * 4;
words_remaining = 0;
}
}
while (words_remaining > 0) {
if (!(address & 0xff))
LOG_DEBUG("0x%" PRIx32 "", address);
/* Program one word */
target_write_u32(target, FLASH_FMA, address);
target_write_buffer(target, FLASH_FMD, 4, buffer);
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_WRITE);
/* LOG_DEBUG("0x%x 0x%x 0x%x",address,buf_get_u32(buffer, 0, 32),FMC_WRKEY | FMC_WRITE); */
/* Wait until write complete */
do {
target_read_u32(target, FLASH_FMC, &flash_fmc);
} while (flash_fmc & FMC_WRITE);
buffer += 4;
address += 4;
words_remaining--;
}
if (bytes_remaining) {
uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff};
/* copy the last remaining bytes into the write buffer */
memcpy(last_word, buffer+bytes_written, bytes_remaining);
if (!(address & 0xff))
LOG_DEBUG("0x%" PRIx32 "", address);
/* Program one word */
target_write_u32(target, FLASH_FMA, address);
target_write_buffer(target, FLASH_FMD, 4, last_word);
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_WRITE);
/* LOG_DEBUG("0x%x 0x%x 0x%x",address,buf_get_u32(buffer, 0, 32),FMC_WRKEY | FMC_WRITE); */
/* Wait until write complete */
do {
target_read_u32(target, FLASH_FMC, &flash_fmc);
} while (flash_fmc & FMC_WRITE);
}
/* Check access violations */
target_read_u32(target, FLASH_CRIS, &flash_cris);
if (flash_cris & (AMASK)) {
LOG_DEBUG("flash_cris 0x%" PRIx32 "", flash_cris);
return ERROR_FLASH_OPERATION_FAILED;
}
return ERROR_OK;
}
static int stellaris_probe(struct flash_bank *bank)
{
struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
int retval;
/* If this is a stellaris chip, it has flash; probe() is just
* to figure out how much is present. Only do it once.
*/
if (stellaris_info->did1 != 0)
return ERROR_OK;
/* stellaris_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.
*/
retval = stellaris_read_part_info(bank);
if (retval != ERROR_OK)
return retval;
free(bank->sectors);
/* provide this for the benefit of the NOR flash framework */
bank->size = stellaris_info->num_pages * stellaris_info->pagesize;
bank->num_sectors = stellaris_info->num_pages;
bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector));
for (unsigned int i = 0; i < bank->num_sectors; i++) {
bank->sectors[i].offset = i * stellaris_info->pagesize;
bank->sectors[i].size = stellaris_info->pagesize;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = -1;
}
return retval;
}
static int stellaris_mass_erase(struct flash_bank *bank)
{
struct target *target = NULL;
struct stellaris_flash_bank *stellaris_info = NULL;
uint32_t flash_fmc;
stellaris_info = bank->driver_priv;
target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (stellaris_info->did1 == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
/* Refresh flash controller timing */
stellaris_read_clock_info(bank);
stellaris_set_flash_timing(bank);
/* Clear and disable flash programming interrupts */
target_write_u32(target, FLASH_CIM, 0);
target_write_u32(target, FLASH_MISC, PMISC | AMISC);
/* REVISIT this clobbers state set by any halted firmware ...
* it might want to process those IRQs.
*/
target_write_u32(target, FLASH_FMA, 0);
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_MERASE);
/* Wait until erase complete */
do {
target_read_u32(target, FLASH_FMC, &flash_fmc);
} while (flash_fmc & FMC_MERASE);
/* if device has > 128k, then second erase cycle is needed
* this is only valid for older devices, but will not hurt */
if (stellaris_info->num_pages * stellaris_info->pagesize > 0x20000) {
target_write_u32(target, FLASH_FMA, 0x20000);
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_MERASE);
/* Wait until erase complete */
do {
target_read_u32(target, FLASH_FMC, &flash_fmc);
} while (flash_fmc & FMC_MERASE);
}
return ERROR_OK;
}
COMMAND_HANDLER(stellaris_handle_mass_erase_command)
{
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
if (stellaris_mass_erase(bank) == ERROR_OK) {
/* set all sectors as erased */
for (unsigned int i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_erased = 1;
command_print(CMD, "stellaris mass erase complete");
} else
command_print(CMD, "stellaris mass erase failed");
return ERROR_OK;
}
/**
* Perform the Stellaris "Recovering a 'Locked' Device procedure.
* This performs a mass erase and then restores all nonvolatile registers
* (including USER_* registers and flash lock bits) to their defaults.
* Accordingly, flash can be reprogrammed, and JTAG can be used.
*
* NOTE that DustDevil parts (at least rev A0 silicon) have errata which
* can affect this operation if flash protection has been enabled.
*/
COMMAND_HANDLER(stellaris_handle_recover_command)
{
struct flash_bank *bank;
struct arm *arm;
int retval;
if (CMD_ARGC != 0)
return ERROR_COMMAND_SYNTAX_ERROR;
bank = get_flash_bank_by_num_noprobe(0);
if (!bank)
return ERROR_FAIL;
/* REVISIT ... it may be worth sanity checking that the AP is
* inactive before we start. ARM documents that switching a DP's
* mode while it's active can cause fault modes that need a power
* cycle to recover.
*/
Jim_Eval_Named(CMD_CTX->interp, "catch { hla_command \"debug unlock\" }", 0, 0);
if (!strcmp(Jim_GetString(Jim_GetResult(CMD_CTX->interp), NULL), "0")) {
retval = ERROR_OK;
goto user_action;
}
/* assert SRST */
if (!(jtag_get_reset_config() & RESET_HAS_SRST)) {
LOG_ERROR("Can't recover Stellaris flash without SRST");
return ERROR_FAIL;
}
adapter_assert_reset();
arm = target_to_arm(bank->target);
for (int i = 0; i < 5; i++) {
retval = dap_to_swd(arm->dap);
if (retval != ERROR_OK)
goto done;
retval = dap_to_jtag(arm->dap);
if (retval != ERROR_OK)
goto done;
}
/* de-assert SRST */
adapter_deassert_reset();
retval = jtag_execute_queue();
/* wait 400+ msec ... OK, "1+ second" is simpler */
usleep(1000);
user_action:
/* USER INTERVENTION required for the power cycle
* Restarting OpenOCD is likely needed because of mode switching.
*/
LOG_INFO("USER ACTION: "
"power cycle Stellaris chip, then restart OpenOCD.");
done:
return retval;
}
static const struct command_registration stellaris_exec_command_handlers[] = {
{
.name = "mass_erase",
.usage = "<bank>",
.handler = stellaris_handle_mass_erase_command,
.mode = COMMAND_EXEC,
.help = "erase entire device",
},
{
.name = "recover",
.handler = stellaris_handle_recover_command,
.mode = COMMAND_EXEC,
.usage = "",
.help = "recover (and erase) locked device",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration stellaris_command_handlers[] = {
{
.name = "stellaris",
.mode = COMMAND_EXEC,
.help = "Stellaris flash command group",
.usage = "",
.chain = stellaris_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
const struct flash_driver stellaris_flash = {
.name = "stellaris",
.commands = stellaris_command_handlers,
.flash_bank_command = stellaris_flash_bank_command,
.erase = stellaris_erase,
.protect = stellaris_protect,
.write = stellaris_write,
.read = default_flash_read,
.probe = stellaris_probe,
.auto_probe = stellaris_probe,
.erase_check = default_flash_blank_check,
.protect_check = stellaris_protect_check,
.info = get_stellaris_info,
.free_driver_priv = default_flash_free_driver_priv,
};