lpc2000: fix LPC810 programming support
[openocd.git] / src / flash / nor / lpc2000.c
index 1aa3b91477b35aefd944c65bb5e72f7c91d66e88..259b42f1c4b7ab784750c5be2ec75c56afafd875 100644 (file)
@@ -18,7 +18,7 @@
  *   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.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
  * lpc1700:
  * - 175x
  * - 176x (tested with LPC1768)
+ *
+ * lpc4300 (also available as lpc1800 - alias)
+ * - 43x2 | 3 | 5 | 7 (tested with LPC4337/LPC4357)
+ * - 18x2 | 3 | 5 | 7
+ *
+ * lpc800:
+ * - 810 | 1 | 2 (tested with LPC810/LPC812)
  */
 
 typedef enum {
        lpc2000_v1,
        lpc2000_v2,
-       lpc1700
+       lpc1700,
+       lpc4300,
+       lpc800,
 } lpc2000_variant;
 
 struct lpc2000_flash_bank {
        lpc2000_variant variant;
        uint32_t cclk;
        int cmd51_dst_boundary;
+       int cmd51_can_64b;
        int cmd51_can_256b;
        int cmd51_can_8192b;
        int calc_checksum;
        uint32_t cmd51_max_buffer;
        int checksum_vector;
+       uint32_t iap_max_stack;
+       uint32_t cmd51_src_offset;
+       uint32_t lpc4300_bank;
 };
 
 enum lpc2000_status_codes {
@@ -97,7 +110,10 @@ enum lpc2000_status_codes {
        LPC2000_INVALID_CODE = 16,
        LPC2000_INVALID_BAUD_RATE = 17,
        LPC2000_INVALID_STOP_BIT = 18,
-       LPC2000_CRP_ENABLED = 19
+       LPC2000_CRP_ENABLED = 19,
+       LPC2000_INVALID_FLASH_UNIT = 20,
+       LPC2000_USER_CODE_CHECKSUM = 21,
+       LCP2000_ERROR_SETTING_ACTIVE_PARTITION = 22,
 };
 
 static int lpc2000_build_sector_list(struct flash_bank *bank)
@@ -213,7 +229,20 @@ static int lpc2000_build_sector_list(struct flash_bank *bank)
                }
        } else if (lpc2000_info->variant == lpc1700) {
                switch (bank->size) {
+                       case 4 * 1024:
+                               lpc2000_info->cmd51_max_buffer = 256;
+                               bank->num_sectors = 1;
+                               break;
+                       case 8 * 1024:
+                               lpc2000_info->cmd51_max_buffer = 512;
+                               bank->num_sectors = 2;
+                               break;
+                       case 16 * 1024:
+                               lpc2000_info->cmd51_max_buffer = 512;
+                               bank->num_sectors = 4;
+                               break;
                        case 32 * 1024:
+                               lpc2000_info->cmd51_max_buffer = 1024;
                                bank->num_sectors = 8;
                                break;
                        case 64 * 1024:
@@ -243,6 +272,63 @@ static int lpc2000_build_sector_list(struct flash_bank *bank)
                        bank->sectors[i].is_erased = -1;
                        bank->sectors[i].is_protected = 1;
                }
+       } else if (lpc2000_info->variant == lpc4300) {
+               switch (bank->size) {
+                       case 256 * 1024:
+                               bank->num_sectors = 11;
+                               break;
+                       case 384 * 1024:
+                               bank->num_sectors = 13;
+                               break;
+                       case 512 * 1024:
+                               bank->num_sectors = 15;
+                               break;
+                       default:
+                               LOG_ERROR("BUG: unknown bank->size encountered");
+                               exit(-1);
+               }
+
+               bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+
+               for (int i = 0; i < bank->num_sectors; i++) {
+                       bank->sectors[i].offset = offset;
+                       /* sectors 0-7 are 8kB-sized, 8 and above are 64kB-sized for LPC43xx devices */
+                       bank->sectors[i].size = (i < 8) ? 8 * 1024 : 64 * 1024;
+                       offset += bank->sectors[i].size;
+                       bank->sectors[i].is_erased = -1;
+                       bank->sectors[i].is_protected = 1;
+               }
+
+       }  else if (lpc2000_info->variant == lpc800) {
+                       lpc2000_info->cmd51_max_buffer = 1024;
+               switch (bank->size) {
+                       case 4 * 1024:
+                               lpc2000_info->cmd51_max_buffer = 256;
+                               bank->num_sectors = 4;
+                               break;
+                       case 8 * 1024:
+                               lpc2000_info->cmd51_max_buffer = 512;
+                               bank->num_sectors = 8;
+                               break;
+                       case 16 * 1024:
+                               bank->num_sectors = 16;
+                               break;
+                       default:
+                               LOG_ERROR("BUG: unknown bank->size encountered");
+                               exit(-1);
+               }
+
+               bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+
+               for (int i = 0; i < bank->num_sectors; i++) {
+                       bank->sectors[i].offset = offset;
+                       /* sectors 0-15 are 1kB-sized for LPC8xx devices */
+                       bank->sectors[i].size = 1 * 1024;
+                       offset += bank->sectors[i].size;
+                       bank->sectors[i].is_erased = -1;
+                       bank->sectors[i].is_protected = 1;
+               }
+
        } else {
                LOG_ERROR("BUG: unknown lpc2000_info->variant encountered");
                exit(-1);
@@ -252,28 +338,30 @@ static int lpc2000_build_sector_list(struct flash_bank *bank)
 }
 
 /* this function allocates and initializes working area used for IAP algorithm
- * uses 180 bytes working area
+ * uses 52 + max IAP stack bytes working area
  * 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait)
  * 0x8 to 0x1f: command parameter table (1+5 words)
  * 0x20 to 0x33: command result table (1+4 words)
- * 0x34 to 0xb3: stack (only 128b needed)
+ * 0x34 to 0xb3|0x104: stack (only 128b needed for lpc17xx/2000, 208 for lpc43xx and 148b for lpc8xx)
  */
 
 static int lpc2000_iap_working_area_init(struct flash_bank *bank, struct working_area **iap_working_area)
 {
        struct target *target = bank->target;
+       struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
 
-       if (target_alloc_working_area(target, 180, iap_working_area) != ERROR_OK) {
+       if (target_alloc_working_area(target, 0x34 + lpc2000_info->iap_max_stack, iap_working_area) != ERROR_OK) {
                LOG_ERROR("no working area specified, can't write LPC2000 internal flash");
                return ERROR_FLASH_OPERATION_FAILED;
        }
 
-       struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
        uint8_t jump_gate[8];
 
        /* write IAP code to working area */
        switch (lpc2000_info->variant) {
+               case lpc800:
                case lpc1700:
+               case lpc4300:
                        target_buffer_set_u32(target, jump_gate, ARMV4_5_T_BX(12));
                        target_buffer_set_u32(target, jump_gate + 4, ARMV5_T_BKPT(0));
                        break;
@@ -301,15 +389,17 @@ static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_wo
                uint32_t param_table[5], uint32_t result_table[4])
 {
        struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
+       struct target *target = bank->target;
 
        struct arm_algorithm arm_algo;  /* for LPC2000 */
        struct armv7m_algorithm armv7m_info;    /* for LPC1700 */
        uint32_t iap_entry_point = 0;   /* to make compiler happier */
 
        switch (lpc2000_info->variant) {
+               case lpc800:
                case lpc1700:
                        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
-                       armv7m_info.core_mode = ARM_MODE_ANY;
+                       armv7m_info.core_mode = ARM_MODE_THREAD;
                        iap_entry_point = 0x1fff1ff1;
                        break;
                case lpc2000_v1:
@@ -319,12 +409,17 @@ static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_wo
                        arm_algo.core_state = ARM_STATE_ARM;
                        iap_entry_point = 0x7ffffff1;
                        break;
+               case lpc4300:
+                       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+                       armv7m_info.core_mode = ARM_MODE_THREAD;
+                       /* read out IAP entry point from ROM driver table at 0x10400100 */
+                       target_read_u32(target, 0x10400100, &iap_entry_point);
+                       break;
                default:
                        LOG_ERROR("BUG: unknown lpc2000->variant encountered");
                        exit(-1);
        }
 
-       struct target *target = bank->target;
        struct mem_param mem_params[2];
 
        /* command parameter table */
@@ -352,10 +447,12 @@ static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_wo
        buf_set_u32(reg_params[2].value, 0, 32, iap_entry_point);
 
        switch (lpc2000_info->variant) {
+               case lpc800:
                case lpc1700:
+               case lpc4300:
                        /* IAP stack */
                        init_reg_param(&reg_params[3], "sp", 32, PARAM_OUT);
-                       buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + 0xb4);
+                       buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + lpc2000_info->cmd51_src_offset);
 
                        /* return address */
                        init_reg_param(&reg_params[4], "lr", 32, PARAM_OUT);
@@ -369,7 +466,7 @@ static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_wo
                case lpc2000_v2:
                        /* IAP stack */
                        init_reg_param(&reg_params[3], "sp_svc", 32, PARAM_OUT);
-                       buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + 0xb4);
+                       buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + lpc2000_info->cmd51_src_offset);
 
                        /* return address */
                        init_reg_param(&reg_params[4], "lr_svc", 32, PARAM_OUT);
@@ -390,7 +487,7 @@ static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_wo
        result_table[3] = target_buffer_get_u32(target, mem_params[1].value + 0x10);
 
        LOG_DEBUG("IAP command = %i (0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32
-                       ") completed with result = %8.8" PRIx32,
+                       ") completed with result = %8.8x",
                        code, param_table[0], param_table[1], param_table[2], param_table[3], param_table[4], status_code);
 
        destroy_mem_param(&mem_params[0]);
@@ -419,6 +516,10 @@ static int lpc2000_iap_blank_check(struct flash_bank *bank, int first, int last)
        if (retval != ERROR_OK)
                return retval;
 
+       struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
+       if (lpc2000_info->variant == lpc4300)
+               param_table[2] = lpc2000_info->lpc4300_bank;
+
        for (int i = first; i <= last && retval == ERROR_OK; i++) {
                /* check single sector */
                param_table[0] = param_table[1] = i;
@@ -460,7 +561,8 @@ FLASH_BANK_COMMAND_HANDLER(lpc2000_flash_bank_command)
        if (CMD_ARGC < 8)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
-       struct lpc2000_flash_bank *lpc2000_info = malloc(sizeof(struct lpc2000_flash_bank));
+       struct lpc2000_flash_bank *lpc2000_info = calloc(1, sizeof(*lpc2000_info));
+
        bank->driver_priv = lpc2000_info;
 
        if (strcmp(CMD_ARGV[6], "lpc2000_v1") == 0) {
@@ -469,28 +571,56 @@ FLASH_BANK_COMMAND_HANDLER(lpc2000_flash_bank_command)
                lpc2000_info->cmd51_can_256b = 0;
                lpc2000_info->cmd51_can_8192b = 1;
                lpc2000_info->checksum_vector = 5;
+               lpc2000_info->iap_max_stack = 128;
        } else if (strcmp(CMD_ARGV[6], "lpc2000_v2") == 0) {
                lpc2000_info->variant = lpc2000_v2;
                lpc2000_info->cmd51_dst_boundary = 256;
                lpc2000_info->cmd51_can_256b = 1;
                lpc2000_info->cmd51_can_8192b = 0;
                lpc2000_info->checksum_vector = 5;
+               lpc2000_info->iap_max_stack = 128;
        } else if (strcmp(CMD_ARGV[6], "lpc1700") == 0) {
                lpc2000_info->variant = lpc1700;
                lpc2000_info->cmd51_dst_boundary = 256;
                lpc2000_info->cmd51_can_256b = 1;
                lpc2000_info->cmd51_can_8192b = 0;
                lpc2000_info->checksum_vector = 7;
+               lpc2000_info->iap_max_stack = 128;
+       } else if (strcmp(CMD_ARGV[6], "lpc1800") == 0 || strcmp(CMD_ARGV[6], "lpc4300") == 0) {
+               lpc2000_info->variant = lpc4300;
+               lpc2000_info->cmd51_dst_boundary = 512;
+               lpc2000_info->cmd51_can_256b = 0;
+               lpc2000_info->cmd51_can_8192b = 0;
+               lpc2000_info->checksum_vector = 7;
+               lpc2000_info->iap_max_stack = 208;
+       } else if (strcmp(CMD_ARGV[6], "lpc800") == 0) {
+               lpc2000_info->variant = lpc800;
+               lpc2000_info->cmd51_dst_boundary = 64;
+               lpc2000_info->cmd51_can_64b = 1;
+               lpc2000_info->cmd51_can_256b = 0;
+               lpc2000_info->cmd51_can_8192b = 0;
+               lpc2000_info->checksum_vector = 7;
+               lpc2000_info->iap_max_stack = 148;
        } else {
                LOG_ERROR("unknown LPC2000 variant: %s", CMD_ARGV[6]);
                free(lpc2000_info);
                return ERROR_FLASH_BANK_INVALID;
        }
 
+       /* see lpc2000_iap_working_area_init() for the reason behind the 0x34 value */
+       lpc2000_info->cmd51_src_offset = 0x34 + lpc2000_info->iap_max_stack;
+
        COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], lpc2000_info->cclk);
        lpc2000_info->calc_checksum = 0;
        lpc2000_build_sector_list(bank);
 
+       uint32_t temp_base = 0;
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], temp_base);
+       if (temp_base >= 0x1B000000)
+               lpc2000_info->lpc4300_bank = 1; /* bank B */
+       else
+               lpc2000_info->lpc4300_bank = 0; /* bank A */
+
        if (CMD_ARGC >= 9) {
                if (strcmp(CMD_ARGV[8], "calc_checksum") == 0)
                        lpc2000_info->calc_checksum = 1;
@@ -511,7 +641,11 @@ static int lpc2000_erase(struct flash_bank *bank, int first, int last)
 
        param_table[0] = first;
        param_table[1] = last;
-       param_table[2] = lpc2000_info->cclk;
+
+       if (lpc2000_info->variant == lpc4300)
+               param_table[2] = lpc2000_info->lpc4300_bank;
+       else
+               param_table[2] = lpc2000_info->cclk;
 
        uint32_t result_table[4];
        struct working_area *iap_working_area;
@@ -521,6 +655,10 @@ static int lpc2000_erase(struct flash_bank *bank, int first, int last)
        if (retval != ERROR_OK)
                return retval;
 
+       if (lpc2000_info->variant == lpc4300)
+               /* Init IAP Anyway */
+               lpc2000_iap_call(bank, iap_working_area, 49, param_table, result_table);
+
        /* Prepare sectors */
        int status_code = lpc2000_iap_call(bank, iap_working_area, 50, param_table, result_table);
        switch (status_code) {
@@ -540,6 +678,10 @@ static int lpc2000_erase(struct flash_bank *bank, int first, int last)
 
        if (retval == ERROR_OK) {
                /* Erase sectors */
+               param_table[2] = lpc2000_info->cclk;
+               if (lpc2000_info->variant == lpc4300)
+                       param_table[3] = lpc2000_info->lpc4300_bank;
+
                status_code = lpc2000_iap_call(bank, iap_working_area, 52, param_table, result_table);
                switch (status_code) {
                        case ERROR_FLASH_OPERATION_FAILED:
@@ -645,6 +787,10 @@ static int lpc2000_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offs
        uint32_t param_table[5] = {0};
        uint32_t result_table[4];
 
+       if (lpc2000_info->variant == lpc4300)
+               /* Init IAP Anyway */
+               lpc2000_iap_call(bank, iap_working_area, 49, param_table, result_table);
+
        while (bytes_remaining > 0) {
                uint32_t thisrun_bytes;
                if (bytes_remaining >= lpc2000_info->cmd51_max_buffer)
@@ -653,12 +799,20 @@ static int lpc2000_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offs
                        thisrun_bytes = 1024;
                else if ((bytes_remaining >= 512) || (!lpc2000_info->cmd51_can_256b))
                        thisrun_bytes = 512;
-               else
+               else if ((bytes_remaining >= 256) || (!lpc2000_info->cmd51_can_64b))
                        thisrun_bytes = 256;
+               else
+                       thisrun_bytes = 64;
 
                /* Prepare sectors */
                param_table[0] = first_sector;
                param_table[1] = last_sector;
+
+               if (lpc2000_info->variant == lpc4300)
+                       param_table[2] = lpc2000_info->lpc4300_bank;
+               else
+                       param_table[2] = lpc2000_info->cclk;
+
                int status_code = lpc2000_iap_call(bank, iap_working_area, 50, param_table, result_table);
                switch (status_code) {
                        case ERROR_FLASH_OPERATION_FAILED:

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)