diff --git a/src/Makefile b/src/Makefile index aef13fb3..6ef91994 100644 --- a/src/Makefile +++ b/src/Makefile @@ -40,6 +40,7 @@ SRC = \ lmi.c \ lpc_common.c \ lpc11xx.c \ + lpc17xx.c \ lpc15xx.c \ lpc43xx.c \ kinetis.c \ diff --git a/src/target/cortexm.c b/src/target/cortexm.c index abd64f4a..ec9608e1 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -299,6 +299,7 @@ bool cortexm_probe(ADIv5_AP_t *ap) PROBE(stm32l4_probe); PROBE(lpc11xx_probe); PROBE(lpc15xx_probe); + PROBE(lpc17xx_probe); PROBE(lpc43xx_probe); PROBE(sam3x_probe); PROBE(sam4l_probe); diff --git a/src/target/jtag_scan.c b/src/target/jtag_scan.c index a30a8050..5487efc0 100644 --- a/src/target/jtag_scan.c +++ b/src/target/jtag_scan.c @@ -61,6 +61,8 @@ static const struct jtag_dev_descr_s { .descr = "ST Microelectronics: STM32F4xx."}, {.idcode = 0x0BB11477 , .idmask = 0xFFFFFFFF, .descr = "NPX: LPC11C24."}, + {.idcode = 0x4BA00477 , .idmask = 0xFFFFFFFF, + .descr = "NXP: LPC17xx family."}, /* Just for fun, unsupported */ {.idcode = 0x8940303F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: ATMega16."}, {.idcode = 0x0792603F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: AT91SAM9261."}, diff --git a/src/target/lpc17xx.c b/src/target/lpc17xx.c new file mode 100644 index 00000000..5aae5a98 --- /dev/null +++ b/src/target/lpc17xx.c @@ -0,0 +1,201 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2012 Gareth McMullin + * + * 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 3 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 . + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" +#include "lpc_common.h" +#include "adiv5.h" + +#define IAP_PGM_CHUNKSIZE 4096 + +#define MIN_RAM_SIZE 8192 // LPC1751 +#define RAM_USAGE_FOR_IAP_ROUTINES 32 // IAP routines use 32 bytes at top of ram + +#define IAP_ENTRYPOINT 0x1FFF1FF1 +#define IAP_RAM_BASE 0x10000000 + +#define ARM_CPUID 0xE000ED00 +#define CORTEX_M3_CPUID 0x412FC230 // Cortex-M3 r2p0 +#define CORTEX_M3_CPUID_MASK 0xFF00FFF0 +#define MEMMAP 0x400FC040 +#define LPC17xx_JTAG_IDCODE 0x4BA00477 +#define LPC17xx_SWDP_IDCODE 0x2BA01477 +#define FLASH_NUM_SECTOR 30 + +struct flash_param { + uint16_t opcode; + uint16_t pad0; + uint32_t command; + uint32_t words[4]; + uint32_t result[5]; // return code and maximum of 4 result parameters +} __attribute__((aligned(4))); + +static void lpc17xx_extended_reset(target *t); +static bool lpc17xx_cmd_erase(target *t, int argc, const char *argv[]); +enum iap_status lpc17xx_iap_call(target *t, struct flash_param *param, enum iap_cmd cmd, ...); + +const struct command_s lpc17xx_cmd_list[] = { + {"erase_mass", lpc17xx_cmd_erase, "Erase entire flash memory"}, + {NULL, NULL, NULL} +}; + +void lpc17xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize, unsigned int base_sector) +{ + struct lpc_flash *lf = lpc_add_flash(t, addr, len); + lf->f.blocksize = erasesize; + lf->base_sector = base_sector; + lf->f.buf_size = IAP_PGM_CHUNKSIZE; + lf->f.write_buf = lpc_flash_write_magic_vect; + lf->iap_entry = IAP_ENTRYPOINT; + lf->iap_ram = IAP_RAM_BASE; + lf->iap_msp = IAP_RAM_BASE + MIN_RAM_SIZE - RAM_USAGE_FOR_IAP_ROUTINES; +} + +bool +lpc17xx_probe(target *t) +{ + /* Read the IDCODE register from the SW-DP */ + ADIv5_AP_t *ap = cortexm_ap(t); + uint32_t ap_idcode = ap->dp->idcode; + + if (ap_idcode == LPC17xx_JTAG_IDCODE || ap_idcode == LPC17xx_SWDP_IDCODE) { + /* LPC176x/5x family. See UM10360.pdf 33.7 JTAG TAP Identification*/ + } else { + return false; + } + + uint32_t cpuid = target_mem_read32(t, ARM_CPUID); + if (((cpuid & CORTEX_M3_CPUID_MASK) == (CORTEX_M3_CPUID & CORTEX_M3_CPUID_MASK))) { + /* + * Now that we're sure it's a Cortex-M3, we need to halt the + * target and make an IAP call to get the part number. + * There appears to have no other method of reading the part number. + */ + target_halt_request(t); + + /* Read the Part ID */ + struct flash_param param; + lpc17xx_iap_call(t, ¶m, IAP_CMD_PARTID); + target_halt_resume(t, 0); + + if (param.result[0]) { + return false; + } + + switch (param.result[1]) { + case 0x26113F37: /* LPC1769 */ + case 0x26013F37: /* LPC1768 */ + case 0x26012837: /* LPC1767 */ + case 0x26013F33: /* LPC1766 */ + case 0x26013733: /* LPC1765 */ + case 0x26011922: /* LPC1764 */ + case 0x25113737: /* LPC1759 */ + case 0x25013F37: /* LPC1758 */ + case 0x25011723: /* LPC1756 */ + case 0x25011722: /* LPC1754 */ + case 0x25001121: /* LPC1752 */ + case 0x25001118: /* LPC1751 */ + case 0x25001110: /* LPC1751 (No CRP) */ + + t->driver = "LPC17xx"; + t->extended_reset = lpc17xx_extended_reset; + target_add_ram(t, 0x10000000, 0x8000); + target_add_ram(t, 0x2007C000, 0x4000); + target_add_ram(t, 0x20080000, 0x4000); + lpc17xx_add_flash(t, 0x00000000, 0x10000, 0x1000, 0); + lpc17xx_add_flash(t, 0x00010000, 0x70000, 0x8000, 16); + target_add_commands(t, lpc17xx_cmd_list, "LPC17xx"); + + return true; + } + } + return false; +} + +static bool +lpc17xx_cmd_erase(target *t, int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + struct flash_param param; + + if (lpc17xx_iap_call(t, ¶m, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR-1)) { + DEBUG("lpc17xx_cmd_erase: prepare failed %d\n", (unsigned int)param.result[0]); + return false; + } + + if (lpc17xx_iap_call(t, ¶m, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR-1, CPU_CLK_KHZ)) { + DEBUG("lpc17xx_cmd_erase: erase failed %d\n", (unsigned int)param.result[0]); + return false; + } + + if (lpc17xx_iap_call(t, ¶m, IAP_CMD_BLANKCHECK, 0, FLASH_NUM_SECTOR-1)) { + DEBUG("lpc17xx_cmd_erase: blankcheck failed %d\n", (unsigned int)param.result[0]); + return false; + } + tc_printf(t, "Erase OK.\n"); + return true; +} + +/** + * Target has been reset, make sure to remap the boot ROM + * from 0x00000000 leaving the user flash visible + */ +static void +lpc17xx_extended_reset(target *t) +{ + /* From ยง33.6 Debug memory re-mapping (Page 643) UM10360.pdf (Rev 2) */ + target_mem_write32(t, MEMMAP, 1); +} + +enum iap_status +lpc17xx_iap_call(target *t, struct flash_param *param, enum iap_cmd cmd, ...) { + param->opcode = ARM_THUMB_BREAKPOINT; + param->command = cmd; + + /* fill out the remainder of the parameters */ + va_list ap; + va_start(ap, cmd); + for (int i = 0; i < 4; i++) + param->words[i] = va_arg(ap, uint32_t); + va_end(ap); + + /* copy the structure to RAM */ + target_mem_write(t, IAP_RAM_BASE, param, sizeof(struct flash_param)); + + /* set up for the call to the IAP ROM */ + uint32_t regs[t->regs_size / sizeof(uint32_t)]; + target_regs_read(t, regs); + regs[0] = IAP_RAM_BASE + offsetof(struct flash_param, command); + regs[1] = IAP_RAM_BASE + offsetof(struct flash_param, result); + regs[REG_MSP] = IAP_RAM_BASE + MIN_RAM_SIZE - RAM_USAGE_FOR_IAP_ROUTINES; + regs[REG_LR] = IAP_RAM_BASE | 1; + regs[REG_PC] = IAP_ENTRYPOINT; + target_regs_write(t, regs); + + /* start the target and wait for it to halt again */ + target_halt_resume(t, false); + while (!target_halt_poll(t, NULL)); + + /* copy back just the parameters structure */ + target_mem_read(t, (void *)param, IAP_RAM_BASE, sizeof(struct flash_param)); + return param->result[0]; +} \ No newline at end of file diff --git a/src/target/lpc_common.h b/src/target/lpc_common.h index dd7dc881..5b132263 100644 --- a/src/target/lpc_common.h +++ b/src/target/lpc_common.h @@ -26,6 +26,7 @@ enum iap_cmd { IAP_CMD_PROGRAM = 51, IAP_CMD_ERASE = 52, IAP_CMD_BLANKCHECK = 53, + IAP_CMD_PARTID = 54, IAP_CMD_SET_ACTIVE_BANK = 60, }; diff --git a/src/target/target_internal.h b/src/target/target_internal.h index 33f16632..b5ebe708 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -175,6 +175,7 @@ bool stm32l4_probe(target *t); bool lmi_probe(target *t); bool lpc11xx_probe(target *t); bool lpc15xx_probe(target *t); +bool lpc17xx_probe(target *t); bool lpc43xx_probe(target *t); bool sam3x_probe(target *t); bool sam4l_probe(target *t);