openocd: fix SPDX tag format for files .c
[fw/openocd] / src / flash / nor / stellaris.c
index 8886c195a8a9f1383180f425ecba8213576022ff..3a78952efda8c102c4a7d18d4b484e7c15d58ba5 100644 (file)
@@ -1,24 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   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, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
  ***************************************************************************/
 
 /***************************************************************************
@@ -32,6 +19,7 @@
 #include "jtag/interface.h"
 #include "imp.h"
 #include <target/algorithm.h>
+#include <target/arm_adi_v5.h>
 #include <target/armv7m.h>
 
 #define DID0_VER(did0) ((did0 >> 28)&0x07)
@@ -69,6 +57,8 @@
 #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
@@ -98,19 +88,16 @@ struct stellaris_flash_bank {
        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;
-       uint32_t flshsz;
        /* flash geometry */
        uint32_t num_pages;
        uint32_t pagesize;
-       uint32_t pages_in_lockregion;
-
-       /* nv memory bits */
-       uint16_t num_lockbits;
 
        /* main clock status */
        uint32_t rcc;
@@ -129,7 +116,7 @@ static const struct {
        uint8_t class;
        uint8_t partno;
        const char *partname;
-} StellarisParts[] = {
+} stellaris_parts[] = {
        {0x00, 0x01, "LM3S101"},
        {0x00, 0x02, "LM3S102"},
        {0x01, 0xBF, "LM3S1110"},
@@ -361,70 +348,96 @@ static const struct {
        {0x06, 0x7D, "LM3S9U90"},
        {0x06, 0x90, "LM3S9U92"},
        {0x06, 0x9B, "LM3S9U96"},
-       {0x05, 0x18, "LM4F110B2QR"},
-       {0x05, 0x19, "LM4F110C4QR"},
-       {0x05, 0x10, "LM4F110E5QR"},
-       {0x05, 0x11, "LM4F110H5QR"},
-       {0x05, 0x22, "LM4F111B2QR"},
-       {0x05, 0x23, "LM4F111C4QR"},
-       {0x05, 0x20, "LM4F111E5QR"},
-       {0x05, 0x21, "LM4F111H5QR"},
-       {0x05, 0x36, "LM4F112C4QC"},
-       {0x05, 0x30, "LM4F112E5QC"},
-       {0x05, 0x31, "LM4F112H5QC"},
-       {0x05, 0x35, "LM4F112H5QD"},
-       {0x05, 0x01, "LM4F120B2QR"},
-       {0x05, 0x02, "LM4F120C4QR"},
-       {0x05, 0x03, "LM4F120E5QR"},
-       {0x05, 0x04, "LM4F120H5QR"},
-       {0x05, 0x08, "LM4F121B2QR"},
-       {0x05, 0x09, "LM4F121C4QR"},
-       {0x05, 0x0A, "LM4F121E5QR"},
-       {0x05, 0x0B, "LM4F121H5QR"},
-       {0x05, 0xD0, "LM4F122C4QC"},
-       {0x05, 0xD1, "LM4F122E5QC"},
-       {0x05, 0xD2, "LM4F122H5QC"},
-       {0x05, 0xD6, "LM4F122H5QD"},
-       {0x05, 0x48, "LM4F130C4QR"},
-       {0x05, 0x40, "LM4F130E5QR"},
-       {0x05, 0x41, "LM4F130H5QR"},
-       {0x05, 0x52, "LM4F131C4QR"},
-       {0x05, 0x50, "LM4F131E5QR"},
-       {0x05, 0x51, "LM4F131H5QR"},
-       {0x05, 0x66, "LM4F132C4QC"},
-       {0x05, 0x60, "LM4F132E5QC"},
-       {0x05, 0x61, "LM4F132H5QC"},
-       {0x05, 0x65, "LM4F132H5QD"},
-       {0x05, 0x70, "LM4F210E5QR"},
-       {0x05, 0x73, "LM4F210H5QR"},
-       {0x05, 0x80, "LM4F211E5QR"},
-       {0x05, 0x83, "LM4F211H5QR"},
-       {0x05, 0xE9, "LM4F212H5BB"},
-       {0x05, 0xC4, "LM4F212H5QC"},
-       {0x05, 0xC6, "LM4F212H5QD"},
-       {0x05, 0xA0, "LM4F230E5QR"},
-       {0x05, 0xA1, "LM4F230H5QR"},
-       {0x05, 0xB0, "LM4F231E5QR"},
-       {0x05, 0xB1, "LM4F231H5QR"},
-       {0x05, 0xC0, "LM4F232E5QC"},
-       {0x05, 0xE3, "LM4F232H5BB"},
-       {0x05, 0xC1, "LM4F232H5QC"},
-       {0x05, 0xC5, "LM4F232H5QD"},
+       {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, 0xE4, "LM4FS99H5BB"},
-       {0x05, 0xE1, "LM4FSXLH5BB"},
+       {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 *StellarisClassname[7] = {
+static const char * const stellaris_classname[] = {
        "Sandstorm",
        "Fury",
        "Unknown",
        "DustDevil",
        "Tempest",
-       "Blizzard",
-       "Firestorm"
+       "Blizzard/TM4C123x",
+       "Firestorm",
+       "",
+       "",
+       "",
+       "Snowflake",
 };
 
 /***************************************************************************
@@ -456,62 +469,45 @@ FLASH_BANK_COMMAND_HANDLER(stellaris_flash_bank_command)
        return ERROR_OK;
 }
 
-static int get_stellaris_info(struct flash_bank *bank, char *buf, int buf_size)
+static int get_stellaris_info(struct flash_bank *bank, struct command_invocation *cmd)
 {
-       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 freqency register */
+       /* 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: %ik, flashsize: %ik\n",
-                          stellaris_info->did1,
-                          stellaris_info->did1,
-                          "ARMv7M",
-                          (int)((1 + ((stellaris_info->dc0 >> 16) & 0xFFFF))/4),
-                          (int)((1 + (stellaris_info->dc0 & 0xFFFF))*2));
-       buf += printed;
-       buf_size -= printed;
-
-       printed = snprintf(buf,
-                          buf_size,
-                          "master clock: %ikHz%s, "
-                          "rcc is 0x%" PRIx32 ", rcc2 is 0x%" PRIx32 "\n",
-                          (int)(stellaris_info->mck_freq / 1000),
-                          stellaris_info->mck_desc,
-                          stellaris_info->rcc,
-                          stellaris_info->rcc2);
-       buf += printed;
-       buf_size -= printed;
-
-       if (stellaris_info->num_lockbits > 0) {
-               snprintf(buf,
-                               buf_size,
-                               "pagesize: %" PRIi32 ", pages: %d, "
-                               "lockbits: %i, pages per lockbit: %i\n",
-                               stellaris_info->pagesize,
-                               (unsigned) stellaris_info->num_pages,
-                               stellaris_info->num_lockbits,
-                               (unsigned) stellaris_info->pages_in_lockregion);
-       }
+       command_print_sameline(cmd,
+                       "\nTI/LMI Stellaris information: Chip is "
+                       "class %i (%s) %s rev %c%i\n",
+                       stellaris_info->target_class,
+                       stellaris_classname[stellaris_info->target_class],
+                       stellaris_info->target_name,
+                       (int)('A' + ((stellaris_info->did0 >> 8) & 0xFF)),
+                       (int)((stellaris_info->did0) & 0xFF));
+
+       command_print_sameline(cmd,
+                       "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));
+
+       command_print_sameline(cmd,
+                       "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;
 }
 
@@ -519,7 +515,7 @@ static int get_stellaris_info(struct flash_bank *bank, char *buf, int buf_size)
 *      chip identification and status                                         *
 ***************************************************************************/
 
-/* Set the flash timimg register to match current clocking */
+/* 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;
@@ -586,7 +582,7 @@ static void stellaris_read_clock_info(struct flash_bank *bank)
        LOG_DEBUG("Stellaris PLLCFG %" PRIx32 "", pllcfg);
 
        stellaris_info->rcc = rcc;
-       stellaris_info->rcc = rcc2;
+       stellaris_info->rcc2 = rcc2;
 
        sysdiv = (rcc >> 23) & 0xF;
        usesysdiv = (rcc >> 22) & 0x1;
@@ -666,7 +662,7 @@ static int stellaris_read_part_info(struct flash_bank *bank)
        LOG_DEBUG("did0 0x%" PRIx32 ", did1 0x%" PRIx32 ", dc0 0x%" PRIx32 ", dc1 0x%" PRIx32 "",
                  did0, did1, stellaris_info->dc0, stellaris_info->dc1);
 
-       ver = did0 >> 28;
+       ver = DID0_VER(did0);
        if ((ver != 0) && (ver != 1)) {
                LOG_WARNING("Unknown did0 version, cannot identify target");
                return ERROR_FLASH_OPERATION_FAILED;
@@ -724,6 +720,7 @@ static int stellaris_read_part_info(struct flash_bank *bank)
                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 */
@@ -736,21 +733,37 @@ static int stellaris_read_part_info(struct flash_bank *bank)
                        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))
+       for (i = 0; stellaris_parts[i].partno; i++) {
+               if ((stellaris_parts[i].partno == ((did1 >> 16) & 0xFF)) &&
+                               (stellaris_parts[i].class == stellaris_info->target_class))
                        break;
        }
 
-       stellaris_info->target_name = StellarisParts[i].partname;
+       stellaris_info->target_name = stellaris_parts[i].partname;
 
        stellaris_info->did0 = did0;
        stellaris_info->did1 = did1;
 
-       stellaris_info->num_lockbits = 1 + (stellaris_info->dc0 & 0xFFFF);
-       stellaris_info->num_pages = 2 * (1 + (stellaris_info->dc0 & 0xFFFF));
-       stellaris_info->pagesize = 1024;
-       stellaris_info->pages_in_lockregion = 2;
+       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
@@ -767,52 +780,54 @@ static int stellaris_read_part_info(struct flash_bank *bank)
 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;
-       unsigned i;
-       unsigned page;
 
        if (stellaris->did1 == 0)
                return ERROR_FLASH_BANK_NOT_PROBED;
 
-       for (i = 0; i < (unsigned) bank->num_sectors; i++)
+       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).
         */
-       for (i = 0, page = 0;
-                       i < DIV_ROUND_UP(stellaris->num_lockbits, 32u);
-                       i++) {
-               uint32_t lockbits;
-
-               status = target_read_u32(bank->target,
-                               SCB_BASE + (i ? (FMPPE0 + 4 * i) : FMPPE),
-                               &lockbits);
-               LOG_DEBUG("FMPPE%d = %#8.8x (status %d)", i,
-                               (unsigned) lockbits, status);
-               if (status != ERROR_OK)
-                       goto done;
 
-               for (unsigned j = 0; j < 32; j++) {
-                       unsigned k;
-
-                       for (k = 0; k < stellaris->pages_in_lockregion; k++) {
-                               if (page >= (unsigned) bank->num_sectors)
-                                       goto done;
-                               bank->sectors[page++].is_protected =
-                                               !(lockbits & (1 << j));
+       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;
        }
 
-done:
        return status;
 }
 
-static int stellaris_erase(struct flash_bank *bank, int first, int last)
+static int stellaris_erase(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
 {
-       int banknr;
        uint32_t flash_fmc, flash_cris;
        struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
        struct target *target = bank->target;
@@ -825,10 +840,10 @@ static int stellaris_erase(struct flash_bank *bank, int first, int last)
        if (stellaris_info->did1 == 0)
                return ERROR_FLASH_BANK_NOT_PROBED;
 
-       if ((first < 0) || (last < first) || (last >= (int)stellaris_info->num_pages))
+       if ((last < first) || (last >= stellaris_info->num_pages))
                return ERROR_FLASH_SECTOR_INVALID;
 
-       if ((first == 0) && (last == ((int)stellaris_info->num_pages-1)))
+       if ((first == 0) && (last == (stellaris_info->num_pages - 1)))
                return stellaris_mass_erase(bank);
 
        /* Refresh flash controller timing */
@@ -843,7 +858,7 @@ static int stellaris_erase(struct flash_bank *bank, int first, int last)
         * it might want to process those IRQs.
         */
 
-       for (banknr = first; banknr <= last; banknr++) {
+       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 */
@@ -853,7 +868,7 @@ static int stellaris_erase(struct flash_bank *bank, int first, int last)
                        target_read_u32(target, FLASH_FMC, &flash_fmc);
                } while (flash_fmc & FMC_ERASE);
 
-               /* Check acess violations */
+               /* 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 "",
@@ -861,22 +876,20 @@ static int stellaris_erase(struct flash_bank *bank, int first, int last)
                        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, int first, int last)
+static int stellaris_protect(struct flash_bank *bank, int set,
+               unsigned int first, unsigned int last)
 {
-       uint32_t fmppe, flash_fmc, flash_cris;
-       int lockregion;
-
-       struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
+       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 (bank->target->state != TARGET_HALTED) {
+       if (target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
@@ -887,14 +900,18 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
-       if (stellaris_info->did1 == 0)
+       if (stellaris->did1 == 0)
                return ERROR_FLASH_BANK_NOT_PROBED;
 
-       /* lockregions are 2 pages ... must protect [even..odd] */
-       if ((first < 0) || (first & 1)
-                       || (last < first) || !(last & 1)
-                       || (last >= 2 * stellaris_info->num_lockbits)) {
-               LOG_ERROR("Can't protect unaligned or out-of-range pages.");
+       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;
        }
 
@@ -902,63 +919,66 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
        stellaris_read_clock_info(bank);
        stellaris_set_flash_timing(bank);
 
-       /* convert from pages to lockregions */
-       first /= 2;
-       last /= 2;
-
-       /* FIXME this assumes single FMPPE, for a max of 64K of flash!!
-        * Current parts can be much bigger.
-        */
-       if (last >= 32) {
-               LOG_ERROR("No support yet for protection > 64K");
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
-
-       target_read_u32(target, SCB_BASE | FMPPE, &fmppe);
-
-       for (lockregion = first; lockregion <= last; lockregion++)
-               fmppe &= ~(1 << lockregion);
-
        /* 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.
-        */
+       uint32_t flash_sizek = stellaris->pagesize / 1024 *
+               stellaris->num_pages;
+       uint32_t fmppe_addr;
 
-       LOG_DEBUG("fmppe 0x%" PRIx32 "", fmppe);
-       target_write_u32(target, SCB_BASE | FMPPE, fmppe);
+       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);
+               /* Commit FMPPE* */
+               target_write_u32(target, FLASH_FMA, 1 + lockbitnum / 16);
+               /* Write commit command */
+               target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT);
 
-       /* Write commit command */
-       /* REVISIT safety check, since this cannot be undone
-        * except by the "Recover a locked device" procedure.
-        * REVISIT DustDevil-A0 parts have an erratum making FMPPE commits
-        * inadvisable ... it makes future mass erase operations fail.
-        */
-       LOG_WARNING("Flash protection cannot be removed once committed, commit is NOT executed !");
-       /* 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);
 
-       /* Wait until erase 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;
+               }
 
-       /* Check acess 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 contib/loaders/flash/stellaris.s for src */
+/* see contrib/loaders/flash/stellaris.s for src */
 
 static const uint8_t stellaris_write_code[] = {
                                                                /* write: */
@@ -997,7 +1017,7 @@ static const uint8_t stellaris_write_code[] = {
        0x01, 0x00, 0x42, 0xA4  /* .word        0xA4420001 */
 };
 static int stellaris_write_block(struct flash_bank *bank,
-               uint8_t *buffer, uint32_t offset, uint32_t wcount)
+               const uint8_t *buffer, uint32_t offset, uint32_t wcount)
 {
        struct target *target = bank->target;
        uint32_t buffer_size = 16384;
@@ -1023,7 +1043,7 @@ static int stellaris_write_block(struct flash_bank *bank,
                        &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)
@@ -1038,7 +1058,7 @@ static int stellaris_write_block(struct flash_bank *bank,
                }
                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),
@@ -1078,7 +1098,7 @@ static int stellaris_write_block(struct flash_bank *bank,
        return retval;
 }
 
-static int stellaris_write(struct flash_bank *bank, uint8_t *buffer,
+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;
@@ -1130,7 +1150,7 @@ static int stellaris_write(struct flash_bank *bank, uint8_t *buffer,
                        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 occured, we examine the reason, and quit */
+                               /* 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);
@@ -1210,16 +1230,13 @@ static int stellaris_probe(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       if (bank->sectors) {
-               free(bank->sectors);
-               bank->sectors = NULL;
-       }
+       free(bank->sectors);
 
        /* provide this for the benefit of the NOR flash framework */
-       bank->size = 1024 * stellaris_info->num_pages;
+       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 (int i = 0; i < bank->num_sectors; i++) {
+       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;
@@ -1281,24 +1298,18 @@ static int stellaris_mass_erase(struct flash_bank *bank)
 
 COMMAND_HANDLER(stellaris_handle_mass_erase_command)
 {
-       int i;
-
        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)
+       if (retval != ERROR_OK)
                return retval;
 
-       if (stellaris_mass_erase(bank) == ERROR_OK) {
-               /* set all sectors as erased */
-               for (i = 0; i < bank->num_sectors; i++)
-                       bank->sectors[i].is_erased = 1;
-
-               command_print(CMD_CTX, "stellaris mass erase complete");
-       } else
-               command_print(CMD_CTX, "stellaris mass erase failed");
+       if (stellaris_mass_erase(bank) == ERROR_OK)
+               command_print(CMD, "stellaris mass erase complete");
+       else
+               command_print(CMD, "stellaris mass erase failed");
 
        return ERROR_OK;
 }
@@ -1315,11 +1326,15 @@ COMMAND_HANDLER(stellaris_handle_mass_erase_command)
 COMMAND_HANDLER(stellaris_handle_recover_command)
 {
        struct flash_bank *bank;
+       struct arm *arm;
        int retval;
 
-       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (retval != ERROR_OK)
-               return 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
@@ -1327,6 +1342,12 @@ COMMAND_HANDLER(stellaris_handle_recover_command)
         * 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");
@@ -1334,12 +1355,13 @@ COMMAND_HANDLER(stellaris_handle_recover_command)
        }
        adapter_assert_reset();
 
+       arm = target_to_arm(bank->target);
        for (int i = 0; i < 5; i++) {
-               retval = dap_to_swd(bank->target);
+               retval = dap_to_swd(arm->dap);
                if (retval != ERROR_OK)
                        goto done;
 
-               retval = dap_to_jtag(bank->target);
+               retval = dap_to_jtag(arm->dap);
                if (retval != ERROR_OK)
                        goto done;
        }
@@ -1351,6 +1373,7 @@ COMMAND_HANDLER(stellaris_handle_recover_command)
        /* 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.
         */
@@ -1373,7 +1396,7 @@ static const struct command_registration stellaris_exec_command_handlers[] = {
                .name = "recover",
                .handler = stellaris_handle_recover_command,
                .mode = COMMAND_EXEC,
-               .usage = "bank_id",
+               .usage = "",
                .help = "recover (and erase) locked device",
        },
        COMMAND_REGISTRATION_DONE
@@ -1389,7 +1412,7 @@ static const struct command_registration stellaris_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
-struct flash_driver stellaris_flash = {
+const struct flash_driver stellaris_flash = {
        .name = "stellaris",
        .commands = stellaris_command_handlers,
        .flash_bank_command = stellaris_flash_bank_command,
@@ -1402,4 +1425,5 @@ struct flash_driver stellaris_flash = {
        .erase_check = default_flash_blank_check,
        .protect_check = stellaris_protect_check,
        .info = get_stellaris_info,
+       .free_driver_priv = default_flash_free_driver_priv,
 };